/***************************************************************************** * Thacker's Tiny Computer 3 in SystemVerilog * ========================================== * Copyright Simon Moore and Frankie Robertson, 2011 *****************************************************************************/ 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; // enumeration types describing fields in the instruction typedef enum logic [2:0] {ADD,SUB,INC,DEC,AND,OR,XOR} Func; typedef enum logic [1:0] {NSH,RCY1,RCY8,RCY16} Rotate; typedef enum logic [1:0] {NSK,LEZ,EQZ,INR} Skip; typedef enum logic [2:0] {FUN,STD,STI,OUT,LDD,IN,JMP} Op; // 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; // function to determine if a skip is to be performed function automatic logic skip_tester(Inst instruction, SignedWord alu_result); return !instruction.lc && (((instruction.payload.skip == LEZ) && (alu_result < 0)) || ((instruction.payload.skip == EQZ) && (alu_result == 0))); endfunction // function to determine the next program counter value function automatic ProgAddr pc_mux(logic jump, logic skip, logic reset_mode, SignedWord alu_result, Word pc); if(reset_mode) return 0; else if (jump) return alu_result[9:0]; else if (skip) return pc + 2; else return pc + 1; endfunction // function to decode the opcode function automatic logic[6:0] opcode_decode(Inst instruction); return (instruction.lc ? 0 : 7'h7f) & {instruction.payload.op == STD, instruction.payload.op == STI, instruction.payload.op == OUT, instruction.payload.op == LDD, instruction.payload.op == IN, instruction.payload.op == JMP, !instruction.payload.op[2]}; endfunction // function implementing the result multiplexer function automatic Word reg_file_write_data_mux(Inst instruction, logic jump, logic load_alu, logic load_dm, logic in, ProgAddr pc, SignedWord alu, Word dm, Word asi_in_data); if (jump) return pc + 1; else if (instruction.lc) return Word'(instruction.payload); else if (load_alu) return alu; else if (load_dm) return dm; else if (in) return asi_in_data; else return 1'bx; endfunction // function to implement the ALU and shifter function automatic SignedWord alu(Inst instruction, Word rfa_out, Word rfb_out); SignedWord pre_shift; unique case (instruction.payload.func) ADD: pre_shift = rfa_out + rfb_out; SUB: pre_shift = rfa_out - rfb_out; INC: pre_shift = rfb_out + 1; DEC: pre_shift = rfb_out - 1; AND: pre_shift = rfa_out & rfb_out; OR: pre_shift = rfa_out | rfb_out; XOR: pre_shift = rfa_out ^ rfb_out; default: pre_shift = 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 module for debug purposes `ifdef MODEL_TECH module DisplayTraces( input clk, input enable, input int trace_level, input Inst i, input Jump, input ProgAddr PC, input Word WD, input Word RFAout, input Word RFBout); ProgAddr oldPC=0; always @(posedge clk) if(enable) if(trace_level==1) begin $write("%05t: ",$time); // output simulation time if(i.lc) // display load-constant instructions $display("PC=0x%02x lc rw=r%02d=%1d",PC,i.rw,WD); else // display other instructions begin $write("PC=0x%02x rw=r%02d=%11d=0x%08x ra=r%02d=0x%08x rb=r%02d=0x%08x", oldPC,i.rw,WD,WD,i.payload.ra,RFAout,i.payload.rb,RFBout); $display(" func=%s rotate=%s skip=%s op=%s", i.payload.func,i.payload.rotate,i.payload.skip,i.payload.op); end if(Jump) $display("----------------------------------------"); if(PC==oldPC) begin $display("STOP condition identified - looping on the spot"); $stop; end oldPC <= PC; end // if (debug_trace==1) else begin if(i.payload.op==IN) $display("%05t: input=0x%08x",$time,WD); if(i.payload.op==OUT) $display("%05t: output=0x%08x",$time,RFAout); end // else: !if(trace_level==1) endmodule `endif module TinyComp( // clock and reset interface input csi_clk_clk, input rsi_reset_reset, // avalon master 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 for IN instructions input Word asi_in_data, input logic asi_in_valid, output logic asi_in_ready, // avalon output stream for OUT instructions output Word aso_out_data, output logic aso_out_valid, input logic aso_out_ready, // exported signal for connection to an activity LED output logic instruction_complete ); // parameters for the instruction ROM initialisation parameter progpath_mif=""; parameter progpath_rmb=""; // parameter to determine the debug level parameter debug_trace=1; // declare variables SignedWord WD; Word RFAout, RFBout; ProgAddr PC, PCmux, im_addr; SignedWord ALU; Word DM; Inst IM, IM_pos; logic doSkip, WriteIM, WriteDM, Jump, LoadDM, LoadALU, In, Out; logic div, phase0, phase1; logic [1:0] div_reset_dly; // instantiate helper module to do tracing in simulation `ifdef MODEL_TECH DisplayTraces dt(csi_clk_clk, phase1, debug_trace, IM_pos, Jump, PCmux, WD, RFAout, RFBout); `endif // instruction memory (ROM) initialised for Quartus (* ram_init_file = progpath_mif *) Inst im_block [0:(1<<$bits(ProgAddr))-1]; initial begin // initialisation of ROM for ModelSim `ifdef MODEL_TECH $readmemb(progpath_rmb, im_block); `endif end always @(posedge csi_clk_clk) im_addr <= PCmux; assign IM = im_block[im_addr]; // implement the register file Word rf_block [0:(1<<$bits(RegAddr))-1]; RegAddr RFA_read_addr, RFB_read_addr; always_ff @(posedge csi_clk_clk) begin // register file port A if (div) rf_block[IM_pos.rw] <= WD; else RFA_read_addr <= IM.payload.ra; end assign RFAout = rf_block[RFA_read_addr]; always_ff @(posedge csi_clk_clk) RFB_read_addr <= IM.payload.rb; assign RFBout = rf_block[RFB_read_addr]; // combinational logic always_comb begin phase0 = !div; phase1 = div && !avm_m1_waitrequest && ((!In && !Out) || (Out && !aso_out_valid) || (In && asi_in_valid)); DM = avm_m1_readdata; ALU = alu(IM_pos, RFAout, RFBout); doSkip = skip_tester(IM_pos, ALU); PCmux = pc_mux(Jump, doSkip, div_reset_dly[1], ALU, PC); WD = reg_file_write_data_mux(IM_pos, Jump, LoadALU, LoadDM, In, PC, ALU, DM, asi_in_data); asi_in_ready = (In && div); avm_m1_write = WriteDM && div; avm_m1_read = LoadDM && div; avm_m1_address = DataAddr'(RFBout); avm_m1_writedata = RFAout; end // the main always block implementing the processor always_ff @(posedge csi_clk_clk) // or posedge rsi_reset_reset) if (rsi_reset_reset) begin div <= 0; div_reset_dly <= 3; PC <= 0; {WriteDM, WriteIM, Out, LoadDM, In, Jump, LoadALU} = 7'b0; aso_out_valid <= 1'b0; end else begin if(aso_out_ready && aso_out_valid) aso_out_valid <= 1'b0; if(phase0) begin IM_pos <= IM; {WriteDM, WriteIM, Out, LoadDM, In, Jump, LoadALU} <= opcode_decode(IM); end if(phase1) begin div <= 1'b0; PC <= PCmux; if(Out) begin aso_out_valid <= 1'b1; aso_out_data <= RFAout; end instruction_complete <= 1'b1; end else begin instruction_complete <= 1'b0; div <= !(|div_reset_dly); div_reset_dly <= {div_reset_dly[0],1'b0}; end end endmodule // test simulation system module TestTinyComp(); // parameters for the instruction ROM initialisation parameter progpath_mif=""; parameter progpath_rmb=""; // parameter to determine the debug level parameter debug_trace=1; logic csi_clk_clk, rsi_reset_reset; logic out_valid; Word out_data; logic in_valid, in_ready; Word in_data; logic [7:0] in_state; logic instruction_complete; TinyComp #( .progpath_mif(progpath_mif), .progpath_rmb(progpath_rmb), .debug_trace(debug_trace) ) dut ( // pass parameters with matching names .*, // null data memory interface .avm_m1_address(), .avm_m1_read(), .avm_m1_readdata(), .avm_m1_write(), .avm_m1_writedata(), .avm_m1_waitrequest(1'b0), // output stream .aso_out_data(out_data), .aso_out_valid(out_valid), .aso_out_ready(1'b1), // input stream .asi_in_data(in_data), .asi_in_valid(in_valid), .asi_in_ready(in_ready) ); // initialise clock and reset initial begin csi_clk_clk = 1; rsi_reset_reset = 1; #20 rsi_reset_reset = 0; end // oscilate the clock always #5 csi_clk_clk = !csi_clk_clk; // test sequencer which sends two values on to the TTC on its In stream // and displays the results from the Out stream always @(posedge csi_clk_clk or posedge rsi_reset_reset) if(rsi_reset_reset) begin in_state <= 0; in_valid <= 0; in_data <= 0; end else begin if(out_valid) $display("%05t: >>>>>>>>>> output = 0x%08x = %1d <<<<<<<<<<",$time,out_data,out_data); if(in_ready) in_valid <= 0; else if(!in_valid) // only send data once the last lot has been consumed begin case(in_state) 0: in_data <= 32'd10; 1: in_data <= 32'd99; 2: in_data <= 32'd2; 3: in_data <= 32'd3; default: in_data <= 32'h0; endcase // case (in_state) if(in_state<4) begin $display("%05t: Test data being sent",$time); in_state <= in_state+1; in_valid <= 1; end end end endmodule // TestTinyComp