I have attempted to implement a 16-bit RISC processor in Verilog, utilizing a two-dimensional register to implement a bank of 16 16-bit registers.
case(opcode) 4'b0000: registers[res] <= registers[reg1] + registers[reg2]; // ADD 4'b0001: registers[res] <= registers[reg1] - registers[reg2]; // SUB 4'b0010: registers[res] <= registers[reg1] * registers[reg2]; // MUL 4'b0011: begin if (registers[reg2] == 16'd0) error <= 1; else registers[res] <= registers[reg1] / registers[reg2]; // DIV end 4'b0100: begin if (registers[reg2] == 16'd0) error <= 1; else registers[res] <= registers[reg1] % registers[reg2]; // MOD end 4'b0101: begin // COMPARISON if (registers[reg1] > registers[reg2]) GLE <= 3'b100; else if (registers[reg1] < registers[reg2]) GLE <= 3'b010; else GLE <= 3'b001; end 4'b0110: registers[res] <= registers[reg1] | registers[reg2]; // OR 4'b0111: registers[res] <= registers[reg1] & registers[reg2]; // AND 4'b1000: registers[res] <= registers[reg1] ^ registers[reg2]; // XOR 4'b1001: registers[res] <= ~registers[reg1]; // NOT 4'b1010: registers[res] <= registers[reg1]; // MOVE 4'b1011: registers[res] <= registers[reg1] << registers[reg2]; // LEFT SHIFT 4'b1100: registers[res] <= registers[reg1] >> registers[reg2]; // RIGHT SHIFT 4'b1101: registers[res] <= memory[reg1]; // LOAD (register = memory) 4'b1110: memory[res] <= registers[reg1]; // STORE (memory = register) endcase All operations are selected using the case structure as shown above. The registers are declared as shown below:
reg [15:0] registers[0:15]; reg [15:0] memory[0:15]; reg error = 0; Here 'registers' is a bank of registers to perform operations with, 'memory' allows for memory storage and 'error' is an error flag.
The problem: none of the registers are updated when a valid opcode passed. For example, if opcode 0000 is passed, along with values for reg1, reg2 and res, nothing happens and 'registers' simply retains the values it was initialised with. Why is this happening?
The problem is specifically with the registers, i was able to confirm all opcodes and values for res, reg1 and reg2 are passed correctly by debugging with $display statements. The case structure lies inside an always block triggered by posedge clock.
Here is the full code for reference:
module RISC16( input clk, input [15:0] in, input [255:0] memin_flat, input [255:0] regin_flat, output reg [2:0] GLE, output reg [255:0] registers_flat, output reg [255:0] memory_flat, output reg error_flag ); reg [3:0] opcode = 4'b0; reg [3:0] reg1 = 4'b0; reg [3:0] reg2 = 4'b0; reg [3:0] res = 4'b0; reg [15:0] registers[0:15]; reg [15:0] memory[0:15]; reg error = 0; integer i; // Clocked process to update registers and memory always @(posedge clk) begin // Unflatten regin_flat into registers for (i = 0; i < 16; i = i + 1) begin registers[i] <= regin_flat[i*16 +: 16]; // Non-blocking assignment (<=) end // Unflatten memin_flat into memory for (i = 0; i < 16; i = i + 1) begin memory[i] <= memin_flat[i*16 +: 16]; // Non-blocking assignment (<=) end // Decode the input instruction opcode <= in[15:12]; // Non-blocking assignments reg1 <= in[11:8]; reg2 <= in[7:4]; res <= in[3:0]; GLE <= 3'b000; error <= 0; // Reset error signal // Perform the operation based on opcode case(opcode) 4'b0000: registers[res] <= registers[reg1] + registers[reg2]; // ADD 4'b0001: registers[res] <= registers[reg1] - registers[reg2]; // SUB 4'b0010: registers[res] <= registers[reg1] * registers[reg2]; // MUL 4'b0011: begin if (registers[reg2] == 16'd0) error <= 1; else registers[res] <= registers[reg1] / registers[reg2]; // DIV end 4'b0100: begin if (registers[reg2] == 16'd0) error <= 1; else registers[res] <= registers[reg1] % registers[reg2]; // MOD end 4'b0101: begin // COMPARISON if (registers[reg1] > registers[reg2]) GLE <= 3'b100; else if (registers[reg1] < registers[reg2]) GLE <= 3'b010; else GLE <= 3'b001; end 4'b0110: registers[res] <= registers[reg1] | registers[reg2]; // OR 4'b0111: registers[res] <= registers[reg1] & registers[reg2]; // AND 4'b1000: registers[res] <= registers[reg1] ^ registers[reg2]; // XOR 4'b1001: registers[res] <= ~registers[reg1]; // NOT 4'b1010: registers[res] <= registers[reg1]; // MOVE 4'b1011: registers[res] <= registers[reg1] << registers[reg2]; // LEFT SHIFT 4'b1100: registers[res] <= registers[reg1] >> registers[reg2]; // RIGHT SHIFT 4'b1101: registers[res] <= memory[reg1]; // LOAD (register = memory) 4'b1110: memory[res] <= registers[reg1]; // STORE (memory = register) endcase $display("Opcode: %b, reg1: %b, reg2: %b, res: %b", opcode, registers[reg1], registers[reg2], registers[res]); // Flatten registers array into registers_flat for (i = 0; i < 16; i = i + 1) begin registers_flat[i*16 +: 16] <= registers[i]; // Non-blocking assignment end // Flatten memory array into memory_flat for (i = 0; i < 16; i = i + 1) begin memory_flat[i*16 +: 16] <= memory[i]; // Non-blocking assignment end // Set error flag error_flag <= error; end endmodule