/** * Step 9: Creating a RISC-V processor * Branches * DONE* */ `default_nettype none `include "clockworks.v" module SOC ( input clk, // system clock input rst_i, // reset button output [4:0] led, // system LEDs input RXD, // UART receive output TXD // UART transmit ); wire clk_i; // internal clock wire resetn; // internal reset signal, goes low on reset // Plug the leds on register 1 to see its contents reg [4:0] leds; assign led = leds; reg [31:0] MEM [0:255]; reg [31:0] PC=0; // program counter reg [31:0] instr; // current instruction `include "riscv_assembly.v" // ========================================================================= // THE KNIGHT RIDER ASSEMBLY PROGRAM // ========================================================================= // Define Label IDs integer GO_LEFT = 1; integer GO_RIGHT = 2; initial begin // --- SETUP --- // x1 = Current LED Pattern (Start at 1) ADDI(x1, x0, 1); // x2 = Left Limit (16 or 0b10000) ADDI(x2, x0, 32); // x3 = Right Limit (1 or 0b00001) ADDI(x3, x0,1); // --- LOOP 1: SHIFT LEFT (1 -> 2 -> 4 -> 8 -> 16) --- Label(GO_LEFT); // If x1 == 16 (Top Limit), Jump to Right Loop BEQ(x1, x2, LabelRef(GO_RIGHT)); // Shift Left by 1 SLLI(x1, x1, 1); // Repeat Left Loop JAL(x0, LabelRef(GO_LEFT)); // --- LOOP 2: SHIFT RIGHT (16 -> 8 -> 4 -> 2 -> 1) --- Label(GO_RIGHT); // If x1 == 1 (Bottom Limit), Jump to Left Loop BEQ(x1, x3, LabelRef(GO_LEFT)); // Shift Right by 1 SRLI(x1, x1, 1); // Repeat Right Loop JAL(x0, LabelRef(GO_RIGHT)); endASM(); end // See the table P. 105 in RISC-V manual // The 10 RISC-V instructions wire isALUreg = (instr[6:0] == 7'b0110011); // rd <- rs1 OP rs2 wire isALUimm = (instr[6:0] == 7'b0010011); // rd <- rs1 OP Iimm wire isBranch = (instr[6:0] == 7'b1100011); // if(rs1 OP rs2) PC<-PC+Bimm wire isJALR = (instr[6:0] == 7'b1100111); // rd <- PC+4; PC<-rs1+Iimm wire isJAL = (instr[6:0] == 7'b1101111); // rd <- PC+4; PC<-PC+Jimm wire isAUIPC = (instr[6:0] == 7'b0010111); // rd <- PC + Uimm wire isLUI = (instr[6:0] == 7'b0110111); // rd <- Uimm wire isLoad = (instr[6:0] == 7'b0000011); // rd <- mem[rs1+Iimm] wire isStore = (instr[6:0] == 7'b0100011); // mem[rs1+Simm] <- rs2 wire isSYSTEM = (instr[6:0] == 7'b1110011); // special // The 5 immediate formats wire [31:0] Uimm={ instr[31], instr[30:12], {12{1'b0}}}; wire [31:0] Iimm={{21{instr[31]}}, instr[30:20]}; wire [31:0] Simm={{21{instr[31]}}, instr[30:25],instr[11:7]}; wire [31:0] Bimm={{20{instr[31]}}, instr[7],instr[30:25],instr[11:8],1'b0}; wire [31:0] Jimm={{12{instr[31]}}, instr[19:12],instr[20],instr[30:21],1'b0}; // Source and destination registers wire [4:0] rs1Id = instr[19:15]; wire [4:0] rs2Id = instr[24:20]; wire [4:0] rdId = instr[11:7]; // function codes wire [2:0] funct3 = instr[14:12]; wire [6:0] funct7 = instr[31:25]; // The registers bank reg [31:0] RegisterBank [0:31]; reg [31:0] rs1; // value of source reg [31:0] rs2; // registers. wire [31:0] writeBackData; // data to be written to rd wire writeBackEn; // asserted if data should be written to rd `ifdef BENCH integer i; initial begin for(i=0; i<32; ++i) begin RegisterBank[i] = 0; end end `endif // The ALU wire [31:0] aluIn1 = rs1; wire [31:0] aluIn2 = isALUreg ? rs2 : Iimm; reg [31:0] aluOut; wire [4:0] shamt = isALUreg ? rs2[4:0] : instr[24:20]; // shift amount // ADD/SUB/ADDI: // funct7[5] is 1 for SUB and 0 for ADD. We need also to test instr[5] // to make the difference with ADDI // // SRLI/SRAI/SRL/SRA: // funct7[5] is 1 for arithmetic shift (SRA/SRAI) and // 0 for logical shift (SRL/SRLI) always @(*) begin case(funct3) 3'b000: aluOut = (funct7[5] & instr[5]) ? (aluIn1 - aluIn2) : (aluIn1 + aluIn2); 3'b001: aluOut = aluIn1 << shamt; 3'b010: aluOut = ($signed(aluIn1) < $signed(aluIn2)); 3'b011: aluOut = (aluIn1 < aluIn2); 3'b100: aluOut = (aluIn1 ^ aluIn2); 3'b101: aluOut = funct7[5]? ($signed(aluIn1) >>> shamt) : ($signed(aluIn1) >> shamt); 3'b110: aluOut = (aluIn1 | aluIn2); 3'b111: aluOut = (aluIn1 & aluIn2); endcase end // The predicate for branch instructions reg takeBranch; always @(*) begin case(funct3) 3'b000: takeBranch = (rs1 == rs2); 3'b001: takeBranch = (rs1 != rs2); 3'b100: takeBranch = ($signed(rs1) < $signed(rs2)); 3'b101: takeBranch = ($signed(rs1) >= $signed(rs2)); 3'b110: takeBranch = (rs1 < rs2); 3'b111: takeBranch = (rs1 >= rs2); default: takeBranch = 1'b0; endcase end // The state machine localparam FETCH_INSTR = 0; localparam FETCH_REGS = 1; localparam EXECUTE = 2; reg [1:0] state = FETCH_INSTR; // register write back assign writeBackData = (isJAL || isJALR) ? (PC + 4) : aluOut; assign writeBackEn = (state == EXECUTE && (isALUreg || isALUimm || isJAL || isJALR) ); // next PC wire [31:0] nextPC = (isBranch && takeBranch) ? PC+Bimm : isJAL ? PC+Jimm : isJALR ? rs1+Iimm : PC+4; always @(posedge clk_i) begin if(!resetn) begin PC <= 0; state <= FETCH_INSTR; end else begin if(writeBackEn && rdId != 0) begin RegisterBank[rdId] <= writeBackData; // For displaying what happens. if(rdId == 1) begin leds <= writeBackData; end `ifdef BENCH $display("x%0d <= %b",rdId,writeBackData); `endif end case(state) FETCH_INSTR: begin instr <= MEM[PC[31:2]]; state <= FETCH_REGS; end FETCH_REGS: begin rs1 <= RegisterBank[rs1Id]; rs2 <= RegisterBank[rs2Id]; state <= EXECUTE; end EXECUTE: begin if(!isSYSTEM) begin PC <= nextPC; end state <= FETCH_INSTR; `ifdef BENCH if(isSYSTEM) $finish(); `endif end endcase end end `ifdef BENCH always @(posedge clk_i) begin if(state == FETCH_REGS) begin case (1'b1) isALUreg: $display( "ALUreg rd=%d rs1=%d rs2=%d funct3=%b", rdId, rs1Id, rs2Id, funct3 ); isALUimm: $display( "ALUimm rd=%d rs1=%d imm=%0d funct3=%b", rdId, rs1Id, Iimm, funct3 ); isBranch: $display("BRANCH rs1=%0d rs2=%0d",rs1Id, rs2Id); isJAL: $display("JAL"); isJALR: $display("JALR"); isAUIPC: $display("AUIPC"); isLUI: $display("LUI"); isLoad: $display("LOAD"); isStore: $display("STORE"); isSYSTEM: $display("SYSTEM"); endcase if(isSYSTEM) begin $finish(); end end end `endif // Gearbox and reset circuitry. Clockworks #( .SLOW(19) // Divide clock frequency by 2^19 )CW( .CLK(clk), .RESET(rst_i), .clk(clk_i), .resetn(resetn) ); assign TXD = 1'b0; // not used for now endmodule