This lesson starts at commit 52ad976f12300a37998296b9861bfa7bccabac4a.
6. Implementing more instructions
After the hopefully fun diversion in the last lesson, we are back to work. There are 40-ish instructions in the RISC-V base instruction set, and we have only implemented two so far. Let's bang out some more instructions!
Let's grab the RV32/64G Instruction Set Listings of the RISC-V docs.
First, let's just add variables for all the bit fields we don't have yet.
|
@@ -31,8 +31,14 @@ begin
|
|
| 31 |
variable funct7: std_logic_vector(6 downto 0);
|
| 32 |
variable rs1, rs2, rd : std_logic_vector(4 downto 0);
|
| 33 |
|
|
|
|
|
|
|
| 34 |
variable i_imm: std_logic_vector(11 downto 0);
|
| 35 |
variable i_imm_s: std_logic_vector(31 downto 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
variable v_decode_output: decode_output_t;
|
| 38 |
begin
|
|
@@ -51,7 +57,14 @@ begin
|
|
| 51 |
funct7 := decode_input.instr(31 downto 25);
|
| 52 |
rd := decode_input.instr(11 downto 7);
|
| 53 |
|
|
|
|
| 54 |
i_imm := decode_input.instr(31 downto 20);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
i_imm_s := std_logic_vector(resize(signed(i_imm), 32));
|
| 56 |
|
| 57 |
v_decode_output := DEFAULT_DECODE_OUTPUT;
|
|
|
|
| 31 |
variable funct7: std_logic_vector(6 downto 0);
|
| 32 |
variable rs1, rs2, rd : std_logic_vector(4 downto 0);
|
| 33 |
|
| 34 |
+
variable b_imm: std_logic_vector(12 downto 0);
|
| 35 |
+
variable b_imm_s: std_logic_vector(31 downto 0);
|
| 36 |
variable i_imm: std_logic_vector(11 downto 0);
|
| 37 |
variable i_imm_s: std_logic_vector(31 downto 0);
|
| 38 |
+
variable j_imm: std_logic_vector(20 downto 0);
|
| 39 |
+
variable j_imm_s: std_logic_vector(31 downto 0);
|
| 40 |
+
variable s_imm: std_logic_vector(11 downto 0);
|
| 41 |
+
variable u_imm: std_logic_vector(31 downto 0);
|
| 42 |
|
| 43 |
variable v_decode_output: decode_output_t;
|
| 44 |
begin
|
|
|
|
| 57 |
funct7 := decode_input.instr(31 downto 25);
|
| 58 |
rd := decode_input.instr(11 downto 7);
|
| 59 |
|
| 60 |
+
b_imm := decode_input.instr(31) & decode_input.instr(7) & decode_input.instr(30 downto 25) & decode_input.instr(11 downto 8) & "0";
|
| 61 |
i_imm := decode_input.instr(31 downto 20);
|
| 62 |
+
j_imm := decode_input.instr(31) & decode_input.instr(19 downto 12) & decode_input.instr(20) & decode_input.instr(30 downto 21) & "0";
|
| 63 |
+
s_imm := decode_input.instr(31 downto 25) & decode_input.instr(11 downto 7);
|
| 64 |
+
u_imm := decode_input.instr(31 downto 12) & "000000000000";
|
| 65 |
+
|
| 66 |
+
-- sign extension
|
| 67 |
+
b_imm_s := std_logic_vector(resize(signed(b_imm), 32));
|
| 68 |
i_imm_s := std_logic_vector(resize(signed(i_imm), 32));
|
| 69 |
|
| 70 |
v_decode_output := DEFAULT_DECODE_OUTPUT;
|
Now, let's remove the decoding logic we currently have and just start implementing the instructions from the RV32/64G Instruction Set Listings.
|
@@ -73,30 +73,7 @@ begin
|
|
| 73 |
v_decode_output.is_active := '1';
|
| 74 |
v_decode_output.is_invalid := '0';
|
| 75 |
|
| 76 |
-
|
| 77 |
-
-- ADDI rd, rs, imm (I-type): sets rd to the sum of rs1 and the sign-extended immediate
|
| 78 |
-
v_decode_output.operation := OP_ADD;
|
| 79 |
-
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 80 |
-
v_decode_output.operand2 := i_imm_s;
|
| 81 |
-
v_decode_output.destination_reg := rd;
|
| 82 |
-
elsif opcode = "0110011" and funct3 = "000" and funct7 = "0000000" then
|
| 83 |
-
-- ADD rd, rs1, rs2 (R-type): sets rd to the sum of rs1 and rs2
|
| 84 |
-
v_decode_output.operation := OP_ADD;
|
| 85 |
-
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 86 |
-
v_decode_output.operand2 := reg(to_integer(unsigned(rs2)));
|
| 87 |
-
v_decode_output.destination_reg := rd;
|
| 88 |
-
elsif opcode = "1111111" and funct3 = "000" then
|
| 89 |
-
-- LED rs1: set the LEDs to the 8 least significant bits of rs1
|
| 90 |
-
v_decode_output.operation := OP_LED;
|
| 91 |
-
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 92 |
-
v_decode_output.operand2 := (others => '0');
|
| 93 |
-
v_decode_output.destination_reg := (others => '0');
|
| 94 |
-
elsif opcode = "1111111" and funct3 = "001" then
|
| 95 |
-
-- HANG
|
| 96 |
-
v_decode_output := DEFAULT_DECODE_OUTPUT;
|
| 97 |
-
else
|
| 98 |
-
v_decode_output.is_invalid := '1';
|
| 99 |
-
end if;
|
| 100 |
else
|
| 101 |
decode_output <= DEFAULT_DECODE_OUTPUT;
|
| 102 |
end if;
|
|
|
|
| 73 |
v_decode_output.is_active := '1';
|
| 74 |
v_decode_output.is_invalid := '0';
|
| 75 |
|
| 76 |
+
-- TODO: implement instruction decoding
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
else
|
| 78 |
decode_output <= DEFAULT_DECODE_OUTPUT;
|
| 79 |
end if;
|
So we start with LUI and AUIPC. In Chapter 2 (RV32I Base Integer Instruction Set) of the RISC-V unprivileged architecture document, we find this:

In chapter 35, we see that the opcode field is 0110111 for LUI and 0010111 for AUIPC.
|
@@ -73,7 +73,13 @@ begin
|
|
| 73 |
v_decode_output.is_active := '1';
|
| 74 |
v_decode_output.is_invalid := '0';
|
| 75 |
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
else
|
| 78 |
decode_output <= DEFAULT_DECODE_OUTPUT;
|
| 79 |
end if;
|
|
|
|
| 73 |
v_decode_output.is_active := '1';
|
| 74 |
v_decode_output.is_invalid := '0';
|
| 75 |
|
| 76 |
+
if opcode = "0110111" then
|
| 77 |
+
-- TODO: LUI
|
| 78 |
+
elsif opcode = "0010111" then
|
| 79 |
+
-- TODO: AUIPC
|
| 80 |
+
else
|
| 81 |
+
v_decode_output.is_invalid := '1';
|
| 82 |
+
end if;
|
| 83 |
else
|
| 84 |
decode_output <= DEFAULT_DECODE_OUTPUT;
|
| 85 |
end if;
|
So, we start with LUI. We need to place the immediate value in the rd register. This is easy enough with the existing infrastructure.
|
@@ -74,7 +74,11 @@ begin
|
|
| 74 |
v_decode_output.is_invalid := '0';
|
| 75 |
|
| 76 |
if opcode = "0110111" then
|
| 77 |
-
--
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
elsif opcode = "0010111" then
|
| 79 |
-- TODO: AUIPC
|
| 80 |
else
|
|
|
|
| 74 |
v_decode_output.is_invalid := '0';
|
| 75 |
|
| 76 |
if opcode = "0110111" then
|
| 77 |
+
-- LUI
|
| 78 |
+
v_decode_output.operation := OP_ADD;
|
| 79 |
+
v_decode_output.operand1 := (others => '0');
|
| 80 |
+
v_decode_output.operand2 := u_imm;
|
| 81 |
+
v_decode_output.destination_reg := rd;
|
| 82 |
elsif opcode = "0010111" then
|
| 83 |
-- TODO: AUIPC
|
| 84 |
else
|
AUIPC is similar, but the it adds the immediate to the address of the instruction. This address is in the pc register in the fetch stage, but not yet available in the decode stage, so we need to pass it in the output of the fetch stage.
|
@@ -7,7 +7,8 @@ use work.core_types.all;
|
|
| 7 |
package core_constants is
|
| 8 |
constant DEFAULT_FETCH_OUTPUT: fetch_output_t := (
|
| 9 |
is_active => '0',
|
| 10 |
-
instr => (others => '0')
|
|
|
|
| 11 |
);
|
| 12 |
|
| 13 |
constant DEFAULT_DECODE_OUTPUT: decode_output_t := (
|
|
|
|
| 7 |
package core_constants is
|
| 8 |
constant DEFAULT_FETCH_OUTPUT: fetch_output_t := (
|
| 9 |
is_active => '0',
|
| 10 |
+
instr => (others => '0'),
|
| 11 |
+
pc => (others => '0')
|
| 12 |
);
|
| 13 |
|
| 14 |
constant DEFAULT_DECODE_OUTPUT: decode_output_t := (
|
|
@@ -34,6 +34,7 @@ begin
|
|
| 34 |
|
| 35 |
output.is_active <= '1';
|
| 36 |
output.instr <= imem(to_integer(pc(5 downto 2)));
|
|
|
|
| 37 |
else
|
| 38 |
output <= DEFAULT_FETCH_OUTPUT;
|
| 39 |
end if;
|
|
|
|
| 34 |
|
| 35 |
output.is_active <= '1';
|
| 36 |
output.instr <= imem(to_integer(pc(5 downto 2)));
|
| 37 |
+
output.pc <= std_logic_vector(pc);
|
| 38 |
else
|
| 39 |
output <= DEFAULT_FETCH_OUTPUT;
|
| 40 |
end if;
|
|
@@ -8,6 +8,7 @@ package core_types is
|
|
| 8 |
type fetch_output_t is record
|
| 9 |
is_active: std_logic;
|
| 10 |
instr: std_logic_vector(31 downto 0);
|
|
|
|
| 11 |
end record fetch_output_t;
|
| 12 |
|
| 13 |
type decode_output_t is record
|
|
|
|
| 8 |
type fetch_output_t is record
|
| 9 |
is_active: std_logic;
|
| 10 |
instr: std_logic_vector(31 downto 0);
|
| 11 |
+
pc: std_logic_vector(31 downto 0);
|
| 12 |
end record fetch_output_t;
|
| 13 |
|
| 14 |
type decode_output_t is record
|
Now, we can implement AUIPC similar to LUI in the decode stage.
|
@@ -80,7 +80,11 @@ begin
|
|
| 80 |
v_decode_output.operand2 := u_imm;
|
| 81 |
v_decode_output.destination_reg := rd;
|
| 82 |
elsif opcode = "0010111" then
|
| 83 |
-
--
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
else
|
| 85 |
v_decode_output.is_invalid := '1';
|
| 86 |
end if;
|
|
|
|
| 80 |
v_decode_output.operand2 := u_imm;
|
| 81 |
v_decode_output.destination_reg := rd;
|
| 82 |
elsif opcode = "0010111" then
|
| 83 |
+
-- AUIPC
|
| 84 |
+
v_decode_output.operation := OP_ADD;
|
| 85 |
+
v_decode_output.operand1 := decode_input.pc;
|
| 86 |
+
v_decode_output.operand2 := u_imm;
|
| 87 |
+
v_decode_output.destination_reg := rd;
|
| 88 |
else
|
| 89 |
v_decode_output.is_invalid := '1';
|
| 90 |
end if;
|
Now, JAL and JALR are control flow instructions, which mean we'll have to somehow change the value of the pc register in the fetch stage. This requires changes in at least 3 modules as well as some testing. This is a bit too much work for this lesson; we'll just work on the "low-hanging fruit" now, and will return to the instructions that require more work later.
|
@@ -85,6 +85,10 @@ begin
|
|
| 85 |
v_decode_output.operand1 := decode_input.pc;
|
| 86 |
v_decode_output.operand2 := u_imm;
|
| 87 |
v_decode_output.destination_reg := rd;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
else
|
| 89 |
v_decode_output.is_invalid := '1';
|
| 90 |
end if;
|
|
|
|
| 85 |
v_decode_output.operand1 := decode_input.pc;
|
| 86 |
v_decode_output.operand2 := u_imm;
|
| 87 |
v_decode_output.destination_reg := rd;
|
| 88 |
+
elsif opcode = "1101111" then
|
| 89 |
+
-- TODO: JAL
|
| 90 |
+
elsif opcode = "1100111" and funct3 = "000" then
|
| 91 |
+
-- TODO: JALR
|
| 92 |
else
|
| 93 |
v_decode_output.is_invalid := '1';
|
| 94 |
end if;
|
Now, BEQ, BNE, BLT, BGE, BLTU, and BGEU are control flow instructions as well, so we skip them too.
|
@@ -89,6 +89,22 @@ begin
|
|
| 89 |
-- TODO: JAL
|
| 90 |
elsif opcode = "1100111" and funct3 = "000" then
|
| 91 |
-- TODO: JALR
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
else
|
| 93 |
v_decode_output.is_invalid := '1';
|
| 94 |
end if;
|
|
|
|
| 89 |
-- TODO: JAL
|
| 90 |
elsif opcode = "1100111" and funct3 = "000" then
|
| 91 |
-- TODO: JALR
|
| 92 |
+
elsif opcode = "1100011" then
|
| 93 |
+
if funct3 = "000" then
|
| 94 |
+
-- TODO: BEQ
|
| 95 |
+
elsif funct3 = "001" then
|
| 96 |
+
-- TODO: BNE
|
| 97 |
+
elsif funct3 = "100" then
|
| 98 |
+
-- TODO: BLT
|
| 99 |
+
elsif funct3 = "101" then
|
| 100 |
+
-- TODO: BGE
|
| 101 |
+
elsif funct3 = "110" then
|
| 102 |
+
-- TODO: BLTU
|
| 103 |
+
elsif funct3 = "111" then
|
| 104 |
+
-- TODO: BGEU
|
| 105 |
+
else
|
| 106 |
+
v_decode_output.is_invalid := '1';
|
| 107 |
+
end if;
|
| 108 |
else
|
| 109 |
v_decode_output.is_invalid := '1';
|
| 110 |
end if;
|
We'll also skip LB, LH, LW, LBU, LHU, SB, SH, SW because these are memory operations and we haven't implemented memory yet. So far, this is going excellent.
|
@@ -105,6 +105,30 @@ begin
|
|
| 105 |
else
|
| 106 |
v_decode_output.is_invalid := '1';
|
| 107 |
end if;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
else
|
| 109 |
v_decode_output.is_invalid := '1';
|
| 110 |
end if;
|
|
|
|
| 105 |
else
|
| 106 |
v_decode_output.is_invalid := '1';
|
| 107 |
end if;
|
| 108 |
+
elsif opcode = "0000011" then
|
| 109 |
+
if funct3 = "000" then
|
| 110 |
+
-- TODO: LB
|
| 111 |
+
elsif funct3 = "001" then
|
| 112 |
+
-- TODO: LH
|
| 113 |
+
elsif funct3 = "010" then
|
| 114 |
+
-- TODO: LW
|
| 115 |
+
elsif funct3 = "100" then
|
| 116 |
+
-- TODO: LBU
|
| 117 |
+
elsif funct3 = "101" then
|
| 118 |
+
-- TODO: LHU
|
| 119 |
+
else
|
| 120 |
+
v_decode_output.is_invalid := '1';
|
| 121 |
+
end if;
|
| 122 |
+
elsif opcode = "0100011" then
|
| 123 |
+
if funct3 = "000" then
|
| 124 |
+
-- TODO: SB
|
| 125 |
+
elsif funct3 = "001" then
|
| 126 |
+
-- TODO: SH
|
| 127 |
+
elsif funct3 = "010" then
|
| 128 |
+
-- TODO: SW
|
| 129 |
+
else
|
| 130 |
+
v_decode_output.is_invalid := '1';
|
| 131 |
+
end if;
|
| 132 |
else
|
| 133 |
v_decode_output.is_invalid := '1';
|
| 134 |
end if;
|
Now, we arrive at a bunch of instructions that have the same opcode (but a different value for the funct3 field): ADDI, SLTI, SLTIU, XORI, ORI, ANDI.
First, we want to recognize these instructions.
|
@@ -129,6 +129,22 @@ begin
|
|
| 129 |
else
|
| 130 |
v_decode_output.is_invalid := '1';
|
| 131 |
end if;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
else
|
| 133 |
v_decode_output.is_invalid := '1';
|
| 134 |
end if;
|
|
|
|
| 129 |
else
|
| 130 |
v_decode_output.is_invalid := '1';
|
| 131 |
end if;
|
| 132 |
+
elsif opcode = "0010011" then
|
| 133 |
+
if funct3 = "000" then
|
| 134 |
+
-- TODO: ADDI
|
| 135 |
+
elsif funct3 = "010" then
|
| 136 |
+
-- TODO: SLTI
|
| 137 |
+
elsif funct3 = "" then
|
| 138 |
+
-- TODO: SLTIU
|
| 139 |
+
elsif funct3 = "" then
|
| 140 |
+
-- TODO: XORI
|
| 141 |
+
elsif funct3 = "" then
|
| 142 |
+
-- TODO: ORI
|
| 143 |
+
elsif funct3 = "" then
|
| 144 |
+
-- TODO: ANDI
|
| 145 |
+
else
|
| 146 |
+
v_decode_output.is_invalid := '1';
|
| 147 |
+
end if;
|
| 148 |
else
|
| 149 |
v_decode_output.is_invalid := '1';
|
| 150 |
end if;
|
We have implemented ADDI before, so we can just add that code back.
|
@@ -131,7 +131,11 @@ begin
|
|
| 131 |
end if;
|
| 132 |
elsif opcode = "0010011" then
|
| 133 |
if funct3 = "000" then
|
| 134 |
-
--
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
elsif funct3 = "010" then
|
| 136 |
-- TODO: SLTI
|
| 137 |
elsif funct3 = "" then
|
|
|
|
| 131 |
end if;
|
| 132 |
elsif opcode = "0010011" then
|
| 133 |
if funct3 = "000" then
|
| 134 |
+
-- ADDI
|
| 135 |
+
v_decode_output.operation := OP_ADD;
|
| 136 |
+
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 137 |
+
v_decode_output.operand2 := i_imm_s;
|
| 138 |
+
v_decode_output.destination_reg := rd;
|
| 139 |
elsif funct3 = "010" then
|
| 140 |
-- TODO: SLTI
|
| 141 |
elsif funct3 = "" then
|
The other instructions are very similar, but the exact operation that is executed in the execute stage is slightly different. So we can structure the code a bit differently to take advantage of the similarity.
|
@@ -130,12 +130,13 @@ begin
|
|
| 130 |
v_decode_output.is_invalid := '1';
|
| 131 |
end if;
|
| 132 |
elsif opcode = "0010011" then
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
if funct3 = "000" then
|
| 134 |
-- ADDI
|
| 135 |
v_decode_output.operation := OP_ADD;
|
| 136 |
-
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 137 |
-
v_decode_output.operand2 := i_imm_s;
|
| 138 |
-
v_decode_output.destination_reg := rd;
|
| 139 |
elsif funct3 = "010" then
|
| 140 |
-- TODO: SLTI
|
| 141 |
elsif funct3 = "" then
|
|
|
|
| 130 |
v_decode_output.is_invalid := '1';
|
| 131 |
end if;
|
| 132 |
elsif opcode = "0010011" then
|
| 133 |
+
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 134 |
+
v_decode_output.operand2 := i_imm_s;
|
| 135 |
+
v_decode_output.destination_reg := rd;
|
| 136 |
+
|
| 137 |
if funct3 = "000" then
|
| 138 |
-- ADDI
|
| 139 |
v_decode_output.operation := OP_ADD;
|
|
|
|
|
|
|
|
|
|
| 140 |
elsif funct3 = "010" then
|
| 141 |
-- TODO: SLTI
|
| 142 |
elsif funct3 = "" then
|
Now we add a couple of operations so that we can decode SLTI, SLTIU, XORI, ORI, ANDI.
|
@@ -138,15 +138,20 @@ begin
|
|
| 138 |
-- ADDI
|
| 139 |
v_decode_output.operation := OP_ADD;
|
| 140 |
elsif funct3 = "010" then
|
| 141 |
-
--
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
elsif funct3 = "" then
|
| 147 |
-
--
|
| 148 |
-
|
| 149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
else
|
| 151 |
v_decode_output.is_invalid := '1';
|
| 152 |
end if;
|
|
|
|
| 138 |
-- ADDI
|
| 139 |
v_decode_output.operation := OP_ADD;
|
| 140 |
elsif funct3 = "010" then
|
| 141 |
+
-- SLTI
|
| 142 |
+
v_decode_output.operation := OP_SLT;
|
| 143 |
+
elsif funct3 = "011" then
|
| 144 |
+
-- SLTIU
|
| 145 |
+
v_decode_output.operation := OP_SLTU;
|
| 146 |
+
elsif funct3 = "100" then
|
| 147 |
+
-- XORI
|
| 148 |
+
v_decode_output.operation := OP_XOR;
|
| 149 |
+
elsif funct3 = "110" then
|
| 150 |
+
-- ORI
|
| 151 |
+
v_decode_output.operation := OP_OR;
|
| 152 |
+
elsif funct3 = "111" then
|
| 153 |
+
-- ANDI
|
| 154 |
+
v_decode_output.operation := OP_AND;
|
| 155 |
else
|
| 156 |
v_decode_output.is_invalid := '1';
|
| 157 |
end if;
|
|
@@ -3,7 +3,7 @@ use ieee.std_logic_1164.all;
|
|
| 3 |
|
| 4 |
|
| 5 |
package core_types is
|
| 6 |
-
type operation_t is (OP_ADD, OP_LED);
|
| 7 |
|
| 8 |
type fetch_output_t is record
|
| 9 |
is_active: std_logic;
|
|
|
|
| 3 |
|
| 4 |
|
| 5 |
package core_types is
|
| 6 |
+
type operation_t is (OP_ADD, OP_SLT, OP_SLTU, OP_XOR, OP_OR, OP_AND, OP_LED);
|
| 7 |
|
| 8 |
type fetch_output_t is record
|
| 9 |
is_active: std_logic;
|
Now we still have to implement these operations in the execute module.
OP_SLT compares two operands as signed numbers, and sets 1 when the first operand is less than the second.
|
@@ -29,6 +29,12 @@ begin
|
|
| 29 |
if input.is_active = '1' and input.is_invalid = '0' then
|
| 30 |
if input.operation = OP_ADD then
|
| 31 |
v_output.result := std_logic_vector(unsigned(input.operand1) + unsigned(input.operand2));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
elsif input.operation = OP_LED then
|
| 33 |
led <= input.operand1(7 downto 0);
|
| 34 |
else
|
|
|
|
| 29 |
if input.is_active = '1' and input.is_invalid = '0' then
|
| 30 |
if input.operation = OP_ADD then
|
| 31 |
v_output.result := std_logic_vector(unsigned(input.operand1) + unsigned(input.operand2));
|
| 32 |
+
elsif input.operation = OP_SLT then
|
| 33 |
+
if signed(input.operand1) < signed(input.operand2) then
|
| 34 |
+
v_output.result := std_logic_vector(to_unsigned(1, 32));
|
| 35 |
+
else
|
| 36 |
+
v_output.result := (others => '0');
|
| 37 |
+
end if;
|
| 38 |
elsif input.operation = OP_LED then
|
| 39 |
led <= input.operand1(7 downto 0);
|
| 40 |
else
|
OP_SLTU is similar but works on unsigned operands.
|
@@ -35,6 +35,12 @@ begin
|
|
| 35 |
else
|
| 36 |
v_output.result := (others => '0');
|
| 37 |
end if;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
elsif input.operation = OP_LED then
|
| 39 |
led <= input.operand1(7 downto 0);
|
| 40 |
else
|
|
|
|
| 35 |
else
|
| 36 |
v_output.result := (others => '0');
|
| 37 |
end if;
|
| 38 |
+
elsif input.operation = OP_SLTU then
|
| 39 |
+
if unsigned(input.operand1) < unsigned(input.operand2) then
|
| 40 |
+
v_output.result := std_logic_vector(to_unsigned(1, 32));
|
| 41 |
+
else
|
| 42 |
+
v_output.result := (others => '0');
|
| 43 |
+
end if;
|
| 44 |
elsif input.operation = OP_LED then
|
| 45 |
led <= input.operand1(7 downto 0);
|
| 46 |
else
|
OP_XOR, OP_OR, and OP_AND are similar and simple to implement.
|
@@ -41,6 +41,12 @@ begin
|
|
| 41 |
else
|
| 42 |
v_output.result := (others => '0');
|
| 43 |
end if;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
elsif input.operation = OP_LED then
|
| 45 |
led <= input.operand1(7 downto 0);
|
| 46 |
else
|
|
|
|
| 41 |
else
|
| 42 |
v_output.result := (others => '0');
|
| 43 |
end if;
|
| 44 |
+
elsif input.operation = OP_XOR then
|
| 45 |
+
v_output.result := input.operand1 xor input.operand2;
|
| 46 |
+
elsif input.operation = OP_OR then
|
| 47 |
+
v_output.result := input.operand1 or input.operand2;
|
| 48 |
+
elsif input.operation = OP_AND then
|
| 49 |
+
v_output.result := input.operand1 and input.operand2;
|
| 50 |
elsif input.operation = OP_LED then
|
| 51 |
led <= input.operand1(7 downto 0);
|
| 52 |
else
|
Now, SLLI, SRLI, and SRAI are similar to eachother but different from the instructions we just implemented, but for some reason all share the same opcode. I'll put these three instructions above the ones we just implemented, so that we can use slightly less logic.
|
@@ -129,6 +129,16 @@ begin
|
|
| 129 |
else
|
| 130 |
v_decode_output.is_invalid := '1';
|
| 131 |
end if;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
elsif opcode = "0010011" then
|
| 133 |
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 134 |
v_decode_output.operand2 := i_imm_s;
|
|
|
|
| 129 |
else
|
| 130 |
v_decode_output.is_invalid := '1';
|
| 131 |
end if;
|
| 132 |
+
elsif opcode = "0010011" and funct3 = "001" and funct7 = "0000000" then
|
| 133 |
+
-- TODO: SLLI
|
| 134 |
+
elsif opcode = "0010011" and funct3 = "101" then
|
| 135 |
+
if funct7 = "0000000" then
|
| 136 |
+
-- TODO: SRLI
|
| 137 |
+
elsif funct7 = "0000001" then
|
| 138 |
+
-- TODO: SRAI
|
| 139 |
+
else
|
| 140 |
+
v_decode_output.is_invalid := '1';
|
| 141 |
+
end if;
|
| 142 |
elsif opcode = "0010011" then
|
| 143 |
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 144 |
v_decode_output.operand2 := i_imm_s;
|
Again, we add operations to implement the decoding of these instructions.
|
@@ -130,12 +130,22 @@ begin
|
|
| 130 |
v_decode_output.is_invalid := '1';
|
| 131 |
end if;
|
| 132 |
elsif opcode = "0010011" and funct3 = "001" and funct7 = "0000000" then
|
| 133 |
-
--
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
elsif opcode = "0010011" and funct3 = "101" then
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
if funct7 = "0000000" then
|
| 136 |
-
--
|
|
|
|
| 137 |
elsif funct7 = "0000001" then
|
| 138 |
-
--
|
|
|
|
| 139 |
else
|
| 140 |
v_decode_output.is_invalid := '1';
|
| 141 |
end if;
|
|
|
|
| 130 |
v_decode_output.is_invalid := '1';
|
| 131 |
end if;
|
| 132 |
elsif opcode = "0010011" and funct3 = "001" and funct7 = "0000000" then
|
| 133 |
+
-- SLLI
|
| 134 |
+
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 135 |
+
v_decode_output.operand2 := "000000000000000000000000000" & rd;
|
| 136 |
+
v_decode_output.destination_reg := rd;
|
| 137 |
+
v_decode_output.operation := OP_SLL;
|
| 138 |
elsif opcode = "0010011" and funct3 = "101" then
|
| 139 |
+
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 140 |
+
v_decode_output.operand2 := "000000000000000000000000000" & rd;
|
| 141 |
+
v_decode_output.destination_reg := rd;
|
| 142 |
+
|
| 143 |
if funct7 = "0000000" then
|
| 144 |
+
-- SRLI
|
| 145 |
+
v_decode_output.operation := OP_SRL;
|
| 146 |
elsif funct7 = "0000001" then
|
| 147 |
+
-- SRAI
|
| 148 |
+
v_decode_output.operation := OP_SRA;
|
| 149 |
else
|
| 150 |
v_decode_output.is_invalid := '1';
|
| 151 |
end if;
|
|
@@ -3,7 +3,7 @@ use ieee.std_logic_1164.all;
|
|
| 3 |
|
| 4 |
|
| 5 |
package core_types is
|
| 6 |
-
type operation_t is (OP_ADD, OP_SLT, OP_SLTU, OP_XOR, OP_OR, OP_AND, OP_LED);
|
| 7 |
|
| 8 |
type fetch_output_t is record
|
| 9 |
is_active: std_logic;
|
|
|
|
| 3 |
|
| 4 |
|
| 5 |
package core_types is
|
| 6 |
+
type operation_t is (OP_ADD, OP_SLT, OP_SLTU, OP_XOR, OP_OR, OP_AND, OP_SLL, OP_SRL, OP_SRA, OP_LED);
|
| 7 |
|
| 8 |
type fetch_output_t is record
|
| 9 |
is_active: std_logic;
|
Then we implement the operations themselves. The shift instructions are a bit weird. The manual mentions
The operand to be shifted is in rs1, and the shift amount is encoded in the lower 5 bits of the I-immediate field.
This means that you can shift by at most 31 bits. Janky as hell in my opinion, but we're just implementing the spec, not making it.
A nice way of implementing shifts by a variable number of bits, say n, is two do the shift as a sequence of shifts of powers of two. If n is 5-bits, and the most significant bit of n is set, we shift by 16 bits. If the next bit is set, we shift by 8 bits, etc., etc. At the end, we'll have shifted by n bits.
|
@@ -21,6 +21,8 @@ begin
|
|
| 21 |
|
| 22 |
process (clk)
|
| 23 |
variable v_output: execute_output_t;
|
|
|
|
|
|
|
| 24 |
begin
|
| 25 |
if rising_edge(clk) then
|
| 26 |
v_output := DEFAULT_EXECUTE_OUTPUT;
|
|
@@ -47,6 +49,61 @@ begin
|
|
| 47 |
v_output.result := input.operand1 or input.operand2;
|
| 48 |
elsif input.operation = OP_AND then
|
| 49 |
v_output.result := input.operand1 and input.operand2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
elsif input.operation = OP_LED then
|
| 51 |
led <= input.operand1(7 downto 0);
|
| 52 |
else
|
|
|
|
| 21 |
|
| 22 |
process (clk)
|
| 23 |
variable v_output: execute_output_t;
|
| 24 |
+
variable v_sign: std_logic_vector(31 downto 0);
|
| 25 |
+
|
| 26 |
begin
|
| 27 |
if rising_edge(clk) then
|
| 28 |
v_output := DEFAULT_EXECUTE_OUTPUT;
|
|
|
|
| 49 |
v_output.result := input.operand1 or input.operand2;
|
| 50 |
elsif input.operation = OP_AND then
|
| 51 |
v_output.result := input.operand1 and input.operand2;
|
| 52 |
+
elsif input.operation = OP_SLL then
|
| 53 |
+
v_output.result := input.operand1;
|
| 54 |
+
|
| 55 |
+
if input.operand2(4) = '1' then
|
| 56 |
+
v_output.result := v_output.result(15 downto 0) & "0000000000000000";
|
| 57 |
+
end if;
|
| 58 |
+
if input.operand2(3) = '1' then
|
| 59 |
+
v_output.result := v_output.result(23 downto 0) & "00000000";
|
| 60 |
+
end if;
|
| 61 |
+
if input.operand2(2) = '1' then
|
| 62 |
+
v_output.result := v_output.result(27 downto 0) & "0000";
|
| 63 |
+
end if;
|
| 64 |
+
if input.operand2(1) = '1' then
|
| 65 |
+
v_output.result := v_output.result(29 downto 0) & "00";
|
| 66 |
+
end if;
|
| 67 |
+
if input.operand2(0) = '1' then
|
| 68 |
+
v_output.result := v_output.result(30 downto 0) & "0";
|
| 69 |
+
end if;
|
| 70 |
+
elsif input.operation = OP_SRL then
|
| 71 |
+
v_output.result := input.operand1;
|
| 72 |
+
|
| 73 |
+
if input.operand2(4) = '1' then
|
| 74 |
+
v_output.result := "0000000000000000" & v_output.result(31 downto 16);
|
| 75 |
+
end if;
|
| 76 |
+
if input.operand2(3) = '1' then
|
| 77 |
+
v_output.result := "00000000" & v_output.result(31 downto 8);
|
| 78 |
+
end if;
|
| 79 |
+
if input.operand2(2) = '1' then
|
| 80 |
+
v_output.result := "0000" & v_output.result(31 downto 4);
|
| 81 |
+
end if;
|
| 82 |
+
if input.operand2(1) = '1' then
|
| 83 |
+
v_output.result := "00" & v_output.result(31 downto 2);
|
| 84 |
+
end if;
|
| 85 |
+
if input.operand2(0) = '1' then
|
| 86 |
+
v_output.result := "0" & v_output.result(31 downto 1);
|
| 87 |
+
end if;
|
| 88 |
+
elsif input.operation = OP_SRA then
|
| 89 |
+
v_output.result := input.operand1;
|
| 90 |
+
v_sign := (others => input.operand1(31));
|
| 91 |
+
|
| 92 |
+
if input.operand2(4) = '1' then
|
| 93 |
+
v_output.result := v_sign(15 downto 0) & v_output.result(31 downto 16);
|
| 94 |
+
end if;
|
| 95 |
+
if input.operand2(3) = '1' then
|
| 96 |
+
v_output.result := v_sign(7 downto 0) & v_output.result(31 downto 8);
|
| 97 |
+
end if;
|
| 98 |
+
if input.operand2(2) = '1' then
|
| 99 |
+
v_output.result := v_sign(3 downto 0) & v_output.result(31 downto 4);
|
| 100 |
+
end if;
|
| 101 |
+
if input.operand2(1) = '1' then
|
| 102 |
+
v_output.result := v_sign(2 downto 0) & v_output.result(31 downto 3);
|
| 103 |
+
end if;
|
| 104 |
+
if input.operand2(0) = '1' then
|
| 105 |
+
v_output.result := v_sign(1 downto 0) & v_output.result(31 downto 2);
|
| 106 |
+
end if;
|
| 107 |
elsif input.operation = OP_LED then
|
| 108 |
led <= input.operand1(7 downto 0);
|
| 109 |
else
|
This is a big change, but it's (almost) the same verbose code. In fact, we can merge the implementations of the two right shifts (SRL and SRA).
|
@@ -67,27 +67,14 @@ begin
|
|
| 67 |
if input.operand2(0) = '1' then
|
| 68 |
v_output.result := v_output.result(30 downto 0) & "0";
|
| 69 |
end if;
|
| 70 |
-
elsif input.operation = OP_SRL then
|
| 71 |
v_output.result := input.operand1;
|
| 72 |
|
| 73 |
-
if input.
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
v_output.result := "00000000" & v_output.result(31 downto 8);
|
| 78 |
-
end if;
|
| 79 |
-
if input.operand2(2) = '1' then
|
| 80 |
-
v_output.result := "0000" & v_output.result(31 downto 4);
|
| 81 |
-
end if;
|
| 82 |
-
if input.operand2(1) = '1' then
|
| 83 |
-
v_output.result := "00" & v_output.result(31 downto 2);
|
| 84 |
-
end if;
|
| 85 |
-
if input.operand2(0) = '1' then
|
| 86 |
-
v_output.result := "0" & v_output.result(31 downto 1);
|
| 87 |
end if;
|
| 88 |
-
elsif input.operation = OP_SRA then
|
| 89 |
-
v_output.result := input.operand1;
|
| 90 |
-
v_sign := (others => input.operand1(31));
|
| 91 |
|
| 92 |
if input.operand2(4) = '1' then
|
| 93 |
v_output.result := v_sign(15 downto 0) & v_output.result(31 downto 16);
|
|
|
|
| 67 |
if input.operand2(0) = '1' then
|
| 68 |
v_output.result := v_output.result(30 downto 0) & "0";
|
| 69 |
end if;
|
| 70 |
+
elsif input.operation = OP_SRL or input.operation = OP_SRA then
|
| 71 |
v_output.result := input.operand1;
|
| 72 |
|
| 73 |
+
if input.operation = OP_SRL then
|
| 74 |
+
v_sign := (others => '0');
|
| 75 |
+
else
|
| 76 |
+
v_sign := (others => input.operand1(31));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
end if;
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
if input.operand2(4) = '1' then
|
| 80 |
v_output.result := v_sign(15 downto 0) & v_output.result(31 downto 16);
|
Moving on; almost all of the instructions with opcode 0110011 are register-register versions of instructions we already implemented. The SUB instruction is the only exception.
As usual I'll add placeholders first.
|
@@ -175,6 +175,30 @@ begin
|
|
| 175 |
else
|
| 176 |
v_decode_output.is_invalid := '1';
|
| 177 |
end if;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
else
|
| 179 |
v_decode_output.is_invalid := '1';
|
| 180 |
end if;
|
|
|
|
| 175 |
else
|
| 176 |
v_decode_output.is_invalid := '1';
|
| 177 |
end if;
|
| 178 |
+
elsif opcode = "0110011" then
|
| 179 |
+
if funct7 = "0000000" and funct3 = "000" then
|
| 180 |
+
-- TODO: ADD
|
| 181 |
+
elsif funct7 = "0100000" and funct3 = "000" then
|
| 182 |
+
-- TODO: SUB
|
| 183 |
+
elsif funct7 = "0000000" and funct3 = "001" then
|
| 184 |
+
-- TODO: SLL
|
| 185 |
+
elsif funct7 = "0000000" and funct3 = "010" then
|
| 186 |
+
-- TODO: SLT
|
| 187 |
+
elsif funct7 = "0000000" and funct3 = "011" then
|
| 188 |
+
-- TODO: SLTU
|
| 189 |
+
elsif funct7 = "0000000" and funct3 = "100" then
|
| 190 |
+
-- TODO: XOR
|
| 191 |
+
elsif funct7 = "0000000" and funct3 = "101" then
|
| 192 |
+
-- TODO: SRL
|
| 193 |
+
elsif funct7 = "0100000" and funct3 = "101" then
|
| 194 |
+
-- TODO: SRA
|
| 195 |
+
elsif funct7 = "0000000" and funct3 = "110" then
|
| 196 |
+
-- TODO: OR
|
| 197 |
+
elsif funct7 = "0000000" and funct3 = "111" then
|
| 198 |
+
-- TODO: AND
|
| 199 |
+
else
|
| 200 |
+
v_decode_output.is_invalid := '1';
|
| 201 |
+
end if;
|
| 202 |
else
|
| 203 |
v_decode_output.is_invalid := '1';
|
| 204 |
end if;
|
|
@@ -3,7 +3,7 @@ use ieee.std_logic_1164.all;
|
|
| 3 |
|
| 4 |
|
| 5 |
package core_types is
|
| 6 |
-
type operation_t is (OP_ADD, OP_SLT, OP_SLTU, OP_XOR, OP_OR, OP_AND, OP_SLL, OP_SRL, OP_SRA, OP_LED);
|
| 7 |
|
| 8 |
type fetch_output_t is record
|
| 9 |
is_active: std_logic;
|
|
|
|
| 3 |
|
| 4 |
|
| 5 |
package core_types is
|
| 6 |
+
type operation_t is (OP_ADD, OP_SLT, OP_SLTU, OP_XOR, OP_OR, OP_AND, OP_SLL, OP_SRL, OP_SRA, OP_SUB, OP_LED);
|
| 7 |
|
| 8 |
type fetch_output_t is record
|
| 9 |
is_active: std_logic;
|
Now we'll add the implementation.
|
@@ -176,26 +176,40 @@ begin
|
|
| 176 |
v_decode_output.is_invalid := '1';
|
| 177 |
end if;
|
| 178 |
elsif opcode = "0110011" then
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
if funct7 = "0000000" and funct3 = "000" then
|
| 180 |
-
--
|
|
|
|
| 181 |
elsif funct7 = "0100000" and funct3 = "000" then
|
| 182 |
-
--
|
|
|
|
| 183 |
elsif funct7 = "0000000" and funct3 = "001" then
|
| 184 |
-
--
|
|
|
|
| 185 |
elsif funct7 = "0000000" and funct3 = "010" then
|
| 186 |
-
--
|
|
|
|
| 187 |
elsif funct7 = "0000000" and funct3 = "011" then
|
| 188 |
-
--
|
|
|
|
| 189 |
elsif funct7 = "0000000" and funct3 = "100" then
|
| 190 |
-
--
|
|
|
|
| 191 |
elsif funct7 = "0000000" and funct3 = "101" then
|
| 192 |
-
--
|
|
|
|
| 193 |
elsif funct7 = "0100000" and funct3 = "101" then
|
| 194 |
-
--
|
|
|
|
| 195 |
elsif funct7 = "0000000" and funct3 = "110" then
|
| 196 |
-
--
|
|
|
|
| 197 |
elsif funct7 = "0000000" and funct3 = "111" then
|
| 198 |
-
--
|
|
|
|
| 199 |
else
|
| 200 |
v_decode_output.is_invalid := '1';
|
| 201 |
end if;
|
|
|
|
| 176 |
v_decode_output.is_invalid := '1';
|
| 177 |
end if;
|
| 178 |
elsif opcode = "0110011" then
|
| 179 |
+
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 180 |
+
v_decode_output.operand2 := reg(to_integer(unsigned(rs2)));
|
| 181 |
+
v_decode_output.destination_reg := rd;
|
| 182 |
+
|
| 183 |
if funct7 = "0000000" and funct3 = "000" then
|
| 184 |
+
-- ADD
|
| 185 |
+
v_decode_output.operation := OP_ADD;
|
| 186 |
elsif funct7 = "0100000" and funct3 = "000" then
|
| 187 |
+
-- SUB
|
| 188 |
+
v_decode_output.operation := OP_SUB;
|
| 189 |
elsif funct7 = "0000000" and funct3 = "001" then
|
| 190 |
+
-- SLL
|
| 191 |
+
v_decode_output.operation := OP_SLL;
|
| 192 |
elsif funct7 = "0000000" and funct3 = "010" then
|
| 193 |
+
-- SLT
|
| 194 |
+
v_decode_output.operation := OP_SLT;
|
| 195 |
elsif funct7 = "0000000" and funct3 = "011" then
|
| 196 |
+
-- SLTU
|
| 197 |
+
v_decode_output.operation := OP_SLTU;
|
| 198 |
elsif funct7 = "0000000" and funct3 = "100" then
|
| 199 |
+
-- XOR
|
| 200 |
+
v_decode_output.operation := OP_XOR;
|
| 201 |
elsif funct7 = "0000000" and funct3 = "101" then
|
| 202 |
+
-- SRL
|
| 203 |
+
v_decode_output.operation := OP_SRL;
|
| 204 |
elsif funct7 = "0100000" and funct3 = "101" then
|
| 205 |
+
-- SRA
|
| 206 |
+
v_decode_output.operation := OP_SRA;
|
| 207 |
elsif funct7 = "0000000" and funct3 = "110" then
|
| 208 |
+
-- OR
|
| 209 |
+
v_decode_output.operation := OP_OR;
|
| 210 |
elsif funct7 = "0000000" and funct3 = "111" then
|
| 211 |
+
-- AND
|
| 212 |
+
v_decode_output.operation := OP_AND;
|
| 213 |
else
|
| 214 |
v_decode_output.is_invalid := '1';
|
| 215 |
end if;
|
Now, we just have to implement OP_SUB in the decoder.
|
@@ -31,6 +31,8 @@ begin
|
|
| 31 |
if input.is_active = '1' and input.is_invalid = '0' then
|
| 32 |
if input.operation = OP_ADD then
|
| 33 |
v_output.result := std_logic_vector(unsigned(input.operand1) + unsigned(input.operand2));
|
|
|
|
|
|
|
| 34 |
elsif input.operation = OP_SLT then
|
| 35 |
if signed(input.operand1) < signed(input.operand2) then
|
| 36 |
v_output.result := std_logic_vector(to_unsigned(1, 32));
|
|
|
|
| 31 |
if input.is_active = '1' and input.is_invalid = '0' then
|
| 32 |
if input.operation = OP_ADD then
|
| 33 |
v_output.result := std_logic_vector(unsigned(input.operand1) + unsigned(input.operand2));
|
| 34 |
+
elsif input.operation = OP_SUB then
|
| 35 |
+
v_output.result := std_logic_vector(unsigned(input.operand1) - unsigned(input.operand2));
|
| 36 |
elsif input.operation = OP_SLT then
|
| 37 |
if signed(input.operand1) < signed(input.operand2) then
|
| 38 |
v_output.result := std_logic_vector(to_unsigned(1, 32));
|
OK, phew. Just a couple of oddball instructions left. FENCE is used for memory ordering. We don't even have memory yet, so for now we'll make this a NOP.
|
@@ -213,6 +213,10 @@ begin
|
|
| 213 |
else
|
| 214 |
v_decode_output.is_invalid := '1';
|
| 215 |
end if;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
else
|
| 217 |
v_decode_output.is_invalid := '1';
|
| 218 |
end if;
|
|
|
|
| 213 |
else
|
| 214 |
v_decode_output.is_invalid := '1';
|
| 215 |
end if;
|
| 216 |
+
elsif funct3 = "000" and opcode = "0001111" then
|
| 217 |
+
-- FENCE
|
| 218 |
+
v_decode_output := DEFAULT_DECODE_OUTPUT;
|
| 219 |
+
v_decode_output.is_active := '1';
|
| 220 |
else
|
| 221 |
v_decode_output.is_invalid := '1';
|
| 222 |
end if;
|
Now, FENCE.TSO and PAUSE are special cases of FENCE, which we already handle. So we can skip them; We'll look if we can do something better later.
ECALL and EBREAK are traps, which we have not implemented yet. So I'll add the logic to be able to easily decode them later, but otherwise ignore them.
|
@@ -217,6 +217,10 @@ begin
|
|
| 217 |
-- FENCE
|
| 218 |
v_decode_output := DEFAULT_DECODE_OUTPUT;
|
| 219 |
v_decode_output.is_active := '1';
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
else
|
| 221 |
v_decode_output.is_invalid := '1';
|
| 222 |
end if;
|
|
|
|
| 217 |
-- FENCE
|
| 218 |
v_decode_output := DEFAULT_DECODE_OUTPUT;
|
| 219 |
v_decode_output.is_active := '1';
|
| 220 |
+
elsif i_imm = "000000000000" and rs1 = "00000" and funct3 = "000" and rd = "00000" and opcode = "1110011" then
|
| 221 |
+
-- ECALL
|
| 222 |
+
elsif i_imm = "000000000001" and rs1 = "00000" and funct3 = "000" and rd = "00000" and opcode = "1110011" then
|
| 223 |
+
-- EBREAK
|
| 224 |
else
|
| 225 |
v_decode_output.is_invalid := '1';
|
| 226 |
end if;
|
Now that we're done, let's add back our custom "LED" and "HANG" instructions.
|
@@ -221,6 +221,15 @@ begin
|
|
| 221 |
-- ECALL
|
| 222 |
elsif i_imm = "000000000001" and rs1 = "00000" and funct3 = "000" and rd = "00000" and opcode = "1110011" then
|
| 223 |
-- EBREAK
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
else
|
| 225 |
v_decode_output.is_invalid := '1';
|
| 226 |
end if;
|
|
|
|
| 221 |
-- ECALL
|
| 222 |
elsif i_imm = "000000000001" and rs1 = "00000" and funct3 = "000" and rd = "00000" and opcode = "1110011" then
|
| 223 |
-- EBREAK
|
| 224 |
+
elsif opcode = "1111111" and funct3 = "000" then
|
| 225 |
+
-- LED (custom instruction): set the LEDs to the 8 least significant bits of rs1
|
| 226 |
+
v_decode_output.operation := OP_LED;
|
| 227 |
+
v_decode_output.operand1 := reg(to_integer(unsigned(rs1)));
|
| 228 |
+
v_decode_output.operand2 := (others => '0');
|
| 229 |
+
v_decode_output.destination_reg := (others => '0');
|
| 230 |
+
elsif opcode = "1111111" and funct3 = "001" then
|
| 231 |
+
-- HANG (custom instruction): stops execution of the CPU
|
| 232 |
+
v_decode_output := DEFAULT_DECODE_OUTPUT;
|
| 233 |
else
|
| 234 |
v_decode_output.is_invalid := '1';
|
| 235 |
end if;
|
Phew, we added a lot of instructions! We added the decoding for all instructions and actually implemented about half of all the instructions in the basic RV32 ISA. Not bad for a single lesson.
Did we forget anything? Testing, you say?