Skip to content
RAKSHITHA R NAYAK edited this page Mar 22, 2024 · 14 revisions

Block Diagram:

Copy of riscv_architecture

Fetching Instruction:

  • The Program Counter (PC) register holds the address of the instruction to be executed.
  • The instruction is fetched from the instruction memory using the address provided by the PC.
  • The fetched instruction is a 32-bit value labeled as Instr.

Reading Registers:

  • The rs1 field of the instruction (bits Instr[19:15]) specifies a register whose value needs to be read. This value is read from the Register File and placed onto port A1, labeled RD1.
  • Similarly, the rs2 field of the instruction (bits Instr[24:20]) specifies another register whose value needs to be read. This value is read from the Register File and placed onto port A2, labeled RD2.

Selecting Source B:

  • A multiplexer is used to choose the source for the second operand (SrcB) of the ALU operation.
  • If the instruction is an R-type instruction, SrcB is chosen from the register file (RD2 port).
  • If the instruction is a Load or Store instruction, SrcB is chosen from ImmExt (immediate value extended). The choice is controlled by the signal ALUSrc.

Performing ALU Operation:

  • The ALU performs the operation on the values obtained from RD1 and SrcB.

Selecting Result:

  • For R-type instructions, the result of the ALU operation (ALUResult) is written back to the Register File.
  • For Load instructions, the result is obtained from the data read from memory (ReadData).
  • Another multiplexer is used to choose between ALUResult and ReadData, controlled by the signal ResultSrc, and the output is labeled as Result.

Fetching Instruction

PC

This is a special register that keeps track of the memory address of the next instruction to be fetched and executed. After each instruction is fetched, the PC is typically incremented to point to the next sequential instruction. The PC is an ordinary 32 bit register. Its output PC points to the current instruction. Its input PC_Next indicates the address of the next instruction.

image

    always @(posedge clk)
    begin
        if(~rst)
            PC <= {32{1'b0}};
        else
            PC <= PC_Next;
    end

PC Adder

While theinstruction is being executed, the processor must compute the address of the next instruction, PCNext. Because instructions are 32 bits = 4 bytes, the next instruction is at PC + 4. Figure below shows that datapath uses another adder to increment the PC by 4. The new address is written into the program counter on the next rising edge of the clock

pc+pcadder drawio

module PC_Adder (a,b,c);

    input [31:0]a,b;
    output [31:0]c;

    assign c = a + b;
    
endmodule

Instruction memory

This is where the program's instructions are stored. It's assumed to have fixed contents, meaning the instructions cannot be modified during normal operation. This type of memory is often referred to as read-only memory (ROM). The instruction memory is accessed by providing it with an address (typically from the PC), and it outputs the instruction stored at that address.

The instruction memory behaves like a combinational circuit, meaning it produces an output based solely on its inputs (the address) without the need for a clock signal. This is because the fetching of instructions doesn't involve any internal state changes or sequential logic; it's a direct mapping from addresses to instructions.

pc drawio (3)

  initial begin
    mem[0] = 32'h0062E233;   //OR     0000_0000_0110_0010_1110_0010_0011_0011   //F3 -110 ->OR   // OR X4,X5,X6
    mem[1] = 32'h00B67433;   //AND    0000_0000_1011_0110_0111_0100_0011_0011   //F3 -010 ->AND   // AND X8,X12,X11
    mem[2] = 32'h00B60933;   //ADD    0000_0000_1011_0110_0000_1001_0011_0011   //F3 -000 ->ADD   // ADD X8,X12,X11
    mem[3] = 32'h41390433;   //SUB    0100_0001_0011_1001_0000_0100_0011_0011   //F3 -000 ->SUB   // SUB X8,X18,X19
    mem[4] = 32'h015A4433;   //XOR    0000_0001_0101_1010_0100_0100_0011_0011   //F3 -100 ->XOR   // XOR X8,X21,X20
    mem[5] = 32'h017B2433;   //SLT    0000_0001_0111_1011_0010_0100_0011_0011   //F3 -010 ->SLT   // SLT X8,X23,X22
  end

Addressing Register File

The 32-element × 32-bit register file has two read ports and one write port. The read ports take 5-bit address inputs, A1 and A2, each specifying one of 2 = 32 registers as source 5 operands. They read the 32-bit register values onto read data outputs RD1 and RD2, respectively. The write port takes a 5-bit address input, A3; a 32-bit write data input, WD; a write enable input, WE3; and a clock. If the write enable is 1, the register file writes the data into the specified register on the rising edge of the clock.

pc drawio (4)

Symbolic Name Number Usage
zero 0 Hard-wired zero
ra 1 Return address
sp 2 Stack pointer
gp 3 Global pointer
tp 4 Thread pointer
t0 5 Temporary / alternative link register
t1 - t2 6 - 7 Temporaries
s0/fp 8 Saved registers/Frame pointer
s1 9 Saved register
a0-a1 10-11 Functional arguments/Return values
a2-a7 12-17 Functional arguments
s2-s11 18-27 Saved registers
t3-t6 28-31 Temporaries
    always @ (posedge clk)
    begin
        if(WE3)
            Register[A3] <= WD3;
    end

    assign RD1 = (~rst) ? 32'd0 : Register[A1];
    assign RD2 = (~rst) ? 32'd0 : Register[A2];

    initial begin
      Register[5] = 32'h00000005;
      Register[6] = 32'h00000004;
      Register[11] = 32'h00000003;
      Register[12] = 32'h00000002;
      Register[18] = 32'h00000005;
      Register[19] = 32'h00000005;
      Register[20] = 32'h00000008;
      Register[21] = 32'h00000009;
      Register[22] = 32'h00000001;
      Register[23] = 32'h00000002;
        
    end

Passing Operands to ALU

Two inputs (operands) are sent to the Arithmetic Logic Unit (ALU) for processing, resulting in an output. The ALU performs operations based on instructions, which are determined by opcode (bits 0 to 6) and function fields (bits 31 to 25 and bits 14 to 12).

passingoperands2alu drawio

Datapath and Control Unit

  • Datapath designed to support data transfers required by instrutions
  • Controller causes correct transfers to happen
  • RISC-V consists of defining the following instruction formats: R-type, I-type, S-Type, B-Type, U-type, and J-type. R-type instructions operate on three registers

Data Path for R type Instruction:

  • R-type instructions use three registers as operands: two as sources, and one as a destination
  • The 32-bit instruction has six fields: op, rs1, rs2, rd, funct3, and funct7.
  • Each field is five or seven bits, as indicated. All R-type instructions have an opcode of 33.
  • R-type instructions can handle add, sub, and, or and slt.
  • All of these instructions read two registers from the register file, perform some ALU operation on them, and write the result back to a third register in the register file.

Instruction Format for some of the R-type instructions is shown below:

RTyPE_inst drawio (1)

Ex: ADD X8,X12,X11

hexa format:

   32'h00B60933;   //    0000_0000_1011_0110_0111_0100_0011_0011

addinst drawio

Block Diagram of Main decoder and ALU decoder

The control unit computes the control signals based on the opcode and funct fields of the instruction, Instr[31:25], Instr[14:12] and Instr[6:0].

decoder drawio

All R-type instructions use the same main decoder values; they differ only in the ALU decoder output

alurtpecontrol drawio

    assign RegWrite = (Op == 7'b0000011 | Op == 7'b0110011) ? 1'b1 :
                                                              1'b0 ;
    assign ImmSrc = (Op == 7'b0100011) ? 2'b01 : 
                    (Op == 7'b1100011) ? 2'b10 :    
                                         2'b00 ;
    assign ALUSrc = (Op == 7'b0000011 | Op == 7'b0100011) ? 1'b1 :
                                                            1'b0 ;
    assign MemWrite = (Op == 7'b0100011) ? 1'b1 :
                                           1'b0 ;
    assign ResultSrc = (Op == 7'b0000011) ? 1'b1 :
                                            1'b0 ;
    assign Branch = (Op == 7'b1100011) ? 1'b1 :
                                         1'b0 ;
    assign ALUOp = (Op == 7'b0110011) ? 2'b10 :
                   (Op == 7'b1100011) ? 2'b01 :
                                        2'b00 ;

The main decoder computes most of the outputs from the opcode. It also determines a 2-bit ALUOp signal.When ALUOp is 00 or 01, the ALU should add or subtract, respectively. When ALUOp is 10, the decoder examines the function fields and operand bit to determine the ALUControl. The control signals for each instruction were described as we built the datapath.

aludecoder drawio

assign ALUControl = (ALUOp == 2'b00) ? 3'b000 :
                        (ALUOp == 2'b01) ? 3'b001 :
                        ((ALUOp == 2'b10) & (funct3 == 3'b000) & ({op[5],funct7[5]} == 2'b11)) ? 3'b001 : 
                        ((ALUOp == 2'b10) & (funct3 == 3'b000) & ({op[5],funct7[5]} != 2'b11)) ? 3'b000 : 
                        ((ALUOp == 2'b10) & (funct3 == 3'b010)) ? 3'b101 : 
                        ((ALUOp == 2'b10) & (funct3 == 3'b110)) ? 3'b011 : 
                        ((ALUOp == 2'b10) & (funct3 == 3'b111)) ? 3'b010 : 
      					((ALUOp == 2'b10) & (funct3 == 3'b100)) ? 3'b111 : 
                                                                  3'b000 ;

Arithmetic Logic Unit (ALU)

alu_block_diagram_with_flag_few_changes_rak drawio (1) drawio

    assign {Cout,Sum} = (ALUControl[0] == 1'b0) ? A + B :
                                          (A + ((~B)+1)) ;
    assign Result = (ALUControl == 3'b000) ? Sum :
                    (ALUControl == 3'b001) ? Sum :
                    (ALUControl == 3'b010) ? A & B :
                    (ALUControl == 3'b011) ? A | B :
                    (ALUControl == 3'b101) ? {{31{1'b0}},(Sum[31])} :
      				(ALUControl == 3'b111) ? A ^ B : {32{1'b0}};
    
    assign OverFlow = ((Sum[31] ^ A[31]) & 
                      (~(ALUControl[0] ^ B[31] ^ A[31])) &
                      (~ALUControl[1]));
    assign Carry = ((~ALUControl[1]) & Cout);
    assign Zero = &(~Result);
    assign Negative = Result[31];