/***************************************************************************** * Thacker's Tiny Computer 3 in SystemVerilog * ============================================================== * Copyright Simon Moore, Frankie Robertson and Ben Thorner, 2012 *****************************************************************************/ typedef logic [6:0] RegAddr; typedef logic [9:0] ProgAddr; typedef logic [9:0] DataAddr; typedef logic [31:0] Word; typedef logic signed [31:0] SignedWord; typedef logic signed [63:0] SignedLong; // enumeration types describing fields in the instruction typedef enum logic [2:0] {ADD,SUB,INC,DEC,AND,OR,XOR,FPMUL} Func; typedef enum logic [1:0] {NSH,RCY1,RCY8,RCY16} Rotate; typedef enum logic [1:0] {NSK,LTZ,EQZ,INR} Skip; typedef enum logic [2:0] {FUN,STD,STI,OUT,LDD,IN,JMP} Op; // enumeration type describing processor phases (instruction-fetch,decode,execute,write-back) typedef enum logic [1:0] {IF,D,E,WB} Phase; // structure describing the instruction format typedef struct packed { RegAddr rw; logic lc; struct packed { RegAddr ra; RegAddr rb; Func func; Rotate rotate; Skip skip; Op op; } payload; } Inst; // helper function to determine the next program counter value function automatic ProgAddr pc_mux(Inst instruction, SignedWord alu, Word pc, logic avm_m1_waitrequest, logic asi_in_valid); if (instruction.lc) return pc + 1; if (instruction.payload.op == JMP) return alu; unique case (instruction.payload.skip) LTZ: if (alu < 0) return pc + 2; EQZ: if (alu == 0) return pc + 2; INR: if (!avm_m1_waitrequest && !(instruction.payload.op == IN && !asi_in_valid)) return pc + 2; default: return pc + 1; endcase; return pc + 1; endfunction // helper function to determine the result to writeback to Rw based on the opcode function automatic Word result_mux(Inst instruction, ProgAddr pc, SignedWord alu, Word dm_result, Word in_result); if (instruction.lc) return Word'(instruction.payload); case (instruction.payload.op) FUN: return alu; STD: return alu; STI: return alu; OUT: return alu; LDD: return dm_result; IN: return in_result; JMP: return pc + 1; default: return 1'bx; endcase; endfunction // helper function to implement the ALU function automatic SignedWord alu(Inst instruction, SignedWord RaVal, SignedWord RbVal); SignedWord pre_shift; unique case (instruction.payload.func) ADD: pre_shift = RaVal + RbVal; SUB: pre_shift = RaVal - RbVal; INC: pre_shift = RbVal + 1; DEC: pre_shift = RbVal - 1; AND: pre_shift = RaVal & RbVal; OR: pre_shift = RaVal | RbVal; XOR: pre_shift = RaVal ^ RbVal; FPMUL: return 1'bx; default: return 1'bx; endcase; case (instruction.payload.rotate) NSH: return pre_shift; RCY1: return {pre_shift,pre_shift} >> 1; RCY8: return {pre_shift,pre_shift} >> 8; RCY16: return {pre_shift,pre_shift} >> 16; endcase; endfunction // helper function indicating whether to stall before writeback function automatic logic stall_mux(Inst instruction, logic avm_m1_waitrequest, logic aso_out_valid, logic asi_in_valid); if (instruction.lc) return 1'b0; unique case (instruction.payload.op) OUT: return aso_out_valid; IN: return !asi_in_valid; STD: return avm_m1_waitrequest; LDD: return avm_m1_waitrequest; default: return 1'b0; endcase; return 1'b0; endfunction module TinyComp( // clock and reset interface input csi_clk_clk, input rsi_reset_reset, // avalon master interface for data memory (unused in labs) output DataAddr avm_m1_address, output logic avm_m1_read, input Word avm_m1_readdata, output logic avm_m1_write, output Word avm_m1_writedata, input logic avm_m1_waitrequest, // avalon input stream interface for IN instructions input Word asi_in_data, input logic asi_in_valid, output logic asi_in_ready, // avalon output stream interface for OUT instructions output Word aso_out_data, output logic aso_out_valid, input logic aso_out_ready ); // parameters for the instruction ROM initIMialisation parameter progpath_mif=""; parameter progpath_rmb=""; // parameter to determine the debug level parameter debug_trace=1; /*** Declare Variables ***/ Inst i; ProgAddr pc, nextPC; Phase phs; SignedWord aluResult; Word RaVal, RbVal; logic resetPending; // register file initialised for Quartus Word rf [0:(1<<$bits(RegAddr))-1]; // instruction memory initialised for Quartus (* ram_init_file = progpath_mif *) Inst IMem [0:(1<<$bits(ProgAddr))-1]; // instruction memory initialised for Modelsim initial begin `ifdef MODEL_TECH $readmemb(progpath_rmb, IMem); `endif end /*** Instantiate sub-modules ***/ `ifdef MODEL_TECH DisplayTraces #( // specify module parameters .debug_trace(debug_trace) ) db ( // pass wires with matching names .*, // processor intermediaries .resultMux(result_mux(i,pc,aluResult,avm_m1_readdata,asi_in_data)), .cycle_finished((phs == WB) && !stall_mux(i,avm_m1_waitrequest,aso_out_valid,asi_in_valid)) ); `endif /*** Define state machine ***/ /*** Main processor state machine *** The instruction is fetched in the IF phase. *** Two registers are fetched in the D phase (irrespective of the instruction type). *** The ALU result is computed, and memory requests initiated in the E phase. *** The PC and result register are modified in the WB phase; *** memory interface control signals are used to acknowledge requests, or output data. *** The WB phase stalls if a required memory interface is not ready. ***/ always_ff @(posedge csi_clk_clk) if (rsi_reset_reset || resetPending) begin // allow last cycle to complete if writing to external memory if (avm_m1_waitrequest) resetPending <= 1'b1; else resetPending <= 1'b0; // reset internal state pc <= 0; phs <= IF; i <= 0; // reset interfaces asi_in_ready <= 1'b0; avm_m1_read <= 1'b0; avm_m1_write <= 1'b0; aso_out_valid <= 1'b0; end else begin if (phs == IF) begin i <= IMem[pc]; phs <= D; end if (phs == D) begin RaVal <= rf [i.payload.ra]; RbVal <= rf [i.payload.rb]; phs <= E; end if (phs == E) begin aluResult <= alu(i, RaVal, RbVal); if (!i.lc) unique case (i.payload.op) LDD: begin avm_m1_read <= 1'b1; avm_m1_address <= DataAddr'(RbVal); end STD: begin avm_m1_write <= 1'b1; avm_m1_address <= DataAddr'(RbVal); avm_m1_writedata <= RaVal; end default: ; // no action endcase; phs <= WB; end if (phs == WB) if (!stall_mux(i,avm_m1_waitrequest,aso_out_valid,asi_in_valid)) begin rf [i.rw] <= result_mux(i,pc,aluResult,avm_m1_readdata,asi_in_data); pc <= pc_mux(i,aluResult,pc,avm_m1_waitrequest,asi_in_valid); if (!i.lc) unique case (i.payload.op) OUT: begin aso_out_valid <= 1'b1; aso_out_data <= RaVal; end IN: asi_in_ready <= 1'b1; LDD: avm_m1_read <= 1'b0; STD: avm_m1_write <= 1'b0; STI: IMem[ProgAddr'(RbVal)] <= RaVal; default: ; // no action endcase; phs <= IF; end if (aso_out_ready && aso_out_valid) aso_out_valid <= 1'b0; if (asi_in_ready && !asi_in_valid) asi_in_ready <= 1'b0; end endmodule /***************************************************************************** * Thacker's Tiny Computer 3 Debug Module in SystemVerilog * ======================================================= * Copyright Simon Moore and Frankie Robertson, 2011 *****************************************************************************/ `ifdef MODEL_TECH module DisplayTraces( input csi_clk_clk, // processor state input Inst i, input ProgAddr pc, input Word RaVal, input Word RbVal, // processor intermediaries input Word resultMux, input cycle_finished ); parameter debug_trace = 0; always @(posedge csi_clk_clk) if (cycle_finished) if (debug_trace) begin $write("%05t: ",$time); if (i.lc) $display("PC=0x%02x lc rw=r%02d=%1d",pc,i.rw,resultMux); else begin $write("PC=0x%02x rw=r%02d=%11d=0x%08x ",pc,i.rw,resultMux,resultMux); $write("ra=r%02d=0x%08x rb=r%02d=0x%08x ",i.payload.ra,RaVal,i.payload.rb,RbVal); $display("func=%s rotate=%s skip=%s op=%s",i.payload.func,i.payload.rotate,i.payload.skip,i.payload.op); end if (i.payload.op == JMP) $display("----------------------------------------"); end else if (!i.lc) begin case (i.payload.op) IN: $display("%05t: input=0x%08x",$time,resultMux); LDD: $display("%05t: input=0x%08x",$time,resultMux); OUT: $display("%05t: output=0x%08x",$time,RaVal); endcase; end endmodule `endif