I'm having a hard time figuring out if the code I wrote is purely combinatorial or sequential logic. I'm designing a simple 16-bit microprocessor (will be implemented on a Spartan 6), and I'm new to Verilog, HDL and FPGAs. The code for the microprocessor is complete, but I'm having second thoughts about the best practices behind the code.
I'm aware that since this is the first time coding for me, the code is not up to any standard, but I tried my best.
One of the most important elements is the ALU, and it has been designed like any other person would. It has overflow/underflow detection which I also asked about here, and I quickly wrote some code for it, which may or may not be correct.
But, my question is whether I should be using the non-blocking operator (<=) or blocking operator (=) in the always
block for the ALU. I know the standard practice is to use the blocking operator while designing combinatorial circuits versus using the non-blocking one, which is better for sequential circuits.
If I was to use blocking operators in the always
block for the ALU, would the synthesized version be slower than if I use non-blocking operators? I plan to use the on-board 100MHz clock on my development board, so I was wondering if the ALU could keep up.
Here's the full code for the ALU:
module alu(clk, rst, en, re, opcode, a_in, b_in, o, z, n, cond, d_out);
// Parameter Definitions
parameter width = 'd16; // ALU Width
// Inputs
input wire clk /* System Clock Input */, rst /* Reset Result Register */, en /* Enables ALU Processing */, re /* ALU Read Enable */;
input wire [4:0] opcode /* 5-bit Operation Code for the ALU. Refer to documentation. */;
input wire signed [width-1:0] a_in /* Operand A Input Port */, b_in /* Operand B Input Port */;
// Outputs
output wire z /* Zero Flag Register (Embedded in res_out) */, n /* Negative/Sign Flag Register */;
output reg o /* Overflow/Underflow/Carry Flag Register */;
output reg cond /* Conditional Flag Register */;
output wire [width-1:0] d_out /* Data Output Port */;
// Internals
reg [1:0] chk_oflow /* Check for Overflow/Underflow */;
reg signed [width+width:0] res_out /* ALU Process Result Register */;
// Flag Logic
assign z = ~|res_out; // Zero Flag
assign n = res_out[15]; // Negative/Sign Flag
assign d_out [width-1:0] = res_out [width-1:0]; // Read Port
// Tri-State Read Control
assign d_out [width-1:0] = (re)?res_out [width-1:0]:0; // Assign d_out Port the value of res_out if re is true.
// Overflow/Underflow Detection Block
always@(chk_oflow) begin
if(rst) o <= 1'b0;
else begin
case(chk_oflow) // synthesis parallel-case
2'b00: o <= 1'b0;
2'b01: begin
if(res_out [width:width-1] == (2'b01 || 2'b10)) o <= 1'b1; // Scenario only possible on Overflow/Underflow.
else o <= 1'b0;
end
2'b10: begin
if((res_out[width+width]) && (~res_out [width+width-1:width-1] != 0)) o <= 1'b1; // Multiplication result is negative.
else if ((~res_out[width+width]) && (res_out [width+width-1:width-1] != 0)) o <= 1'b1; // Multiplication result is positive.
else o <= 1'b0;
end
2'b11: o <= 1'b0;
default: o <= 1'b0;
endcase
end
end
// ALU Processing Block
always@(posedge clk) begin
if(en && !rst) begin
case(opcode) // synthesis parallel-case
5'b00000: begin
res_out [width-1:0] <= a_in [width-1:0]; // A
end
5'b00001: begin
res_out [width-1:0] <= b_in [width-1:0]; // B
end
5'b00010: begin
res_out [width-1:0] <= a_in [width-1:0] + 1'b1; // Increment A
end
5'b00011: begin
res_out [width-1:0] <= b_in [width-1:0] + 1'b1; // Increment B
end
5'b00100: begin
res_out [width-1:0] <= a_in [width-1:0] - 1'b1; // Decrement A
end
5'b00101: begin
res_out [width-1:0] <= b_in [width-1:0] - 1'b1; // Decrement B
end
5'b00110: begin
chk_oflow <= 2'b01;
res_out [width:0] <= {a_in[width-1], a_in [width-1:0]} + {b_in[width-1], b_in [width-1:0]}; // Add A + B
end
5'b00111: begin
chk_oflow <= 2'b01;
res_out [width:0] <= {a_in[width-1], a_in [width-1:0]} - {b_in[width-1], b_in [width-1:0]}; // Subtract A - B
end
5'b01000: begin
chk_oflow <= 2'b10;
res_out [width+width:0] <= a_in [width-1:0] * b_in [width-1:0]; // Multiply A * B
end
5'b01001: begin
res_out [width-1:0] <= ~a_in [width-1:0]; // One's Complement of A
end
5'b01010: begin
res_out [width-1:0] <= ~b_in [width-1:0]; // One's Complement of B
end
5'b01011: begin
res_out [width-1:0] <= ~a_in [width-1:0] + 1'b1; // Two's Complement of A
end
5'b01100: begin
res_out [width-1:0] <= ~b_in [width-1:0] + 1'b1; // Two's Complement of B
end
5'b01101: begin
if(a_in [width-1:0] == b_in [width-1:0]) cond <= 1'b1; // Compare A == B, set Conditional Register as result
else cond <= 1'b0;
end
5'b01110: begin
if(a_in [width-1:0] < b_in [width-1:0]) cond <= 1'b1; // Compare A < B, set Conditional Register as result
else cond <= 1'b0;
end
5'b01111: begin
if(a_in [width-1:0] > b_in [width-1:0]) cond <= 1'b1;// Compare A > B, set Conditional Register as result
else cond <= 1'b0;
end
5'b10000: begin
res_out [width-1:0] <= a_in [width-1:0] & b_in [width-1:0]; // Bitwise AND
end
5'b10001: begin
res_out [width-1:0] <= a_in [width-1:0] | b_in [width-1:0]; // Bitwise OR
end
5'b10010: begin
res_out [width-1:0] <= a_in [width-1:0] ^ b_in [width-1:0]; // Bitwise XOR
end
5'b10011: begin
res_out [width-1:0] <= a_in [width-1:0] ~& b_in [width-1:0]; // Bitwise NAND
end
5'b10100: begin
res_out [width-1:0] <= a_in [width-1:0] ~| b_in [width-1:0]; // Bitwise NOR
end
5'b10101: begin
res_out [width-1:0] <= a_in [width-1:0] ~^ b_in [width-1:0]; // Bitwise XNOR
end
5'b10110: begin
res_out [width-1:0] <= {a_in [width-2:0], 1'b0}; // Logical Left Shift A
end
5'b10111: begin
res_out [width-1:0] <= {b_in [width-2:0], 1'b0}; // Logical Left Shift B
end
5'b11000: begin
res_out [width-1:0] <= {1'b0, a_in [width-1:1]}; // Logical Right Shift A
end
5'b11001: begin
res_out [width-1:0] <= {1'b0, b_in [width-1:1]}; // Logical Right Shift B
end
5'b11010: begin
res_out [width-1:0] <= {a_in [width-1], a_in [width-1:1]}; // Arithmetic Right Shift A
end
5'b11011: begin
res_out [width-1:0] <= {b_in [width-1], b_in [width-1:1]}; // Arithmetic Right Shift B
end
5'b11100: begin
res_out [width-1:0] <= {a_in [width-2:0], a_in [width-1]}; // Rotate Left A
end
5'b11101: begin
res_out [width-1:0] <= {b_in [width-2:0], b_in [width-1]}; // Rotate Left B
end
5'b11110: begin
res_out [width-1:0] <= {a_in [0], a_in [width-1:1]}; // Rotate Right A
end
5'b11111: begin
res_out [width-1:0] <= {b_in [0], b_in [width-1:1]}; // Rotate Right B
end
default: begin
cond <= 1'b0;
res_out [width-1:0] <= 0;
end
end else if(rst) begin
cond <= 1'b0;
chk_oflow <= 2'b0;
res_out [width-1:0] <= 0;
end
end
endmodule
There is a good chance I'll reduce the number of operations it performs to reduce the amount of unnecessary operations it does on both A and B, since less operations translates to better RISC performance.
Would a blocking or non-blocking operator be appropriate in this case?