8
\$\begingroup\$

I'm an EE student who's taken a a couple digital logic/design courses, but they were focused on schematic representation, so I'm teaching myself Verilog to implement what I've learned.

For a basic "Hello World" project, I've implemented a basic 8-bit ALU with addition/subtraction with carry/borrow, NOT, AND, OR, XOR, and carry/zero/sign flags.

I've implemented it on an FPGA and everything works, but I want to make sure I'm not falling for any beginner traps which can lead to dramatic/undesired results in the final implementation of the circuit.

module ALU(A, B, Cin, Op, Out, Cout, Zero, Sign);
 input[7:0] A, B;
 input Cin;
 //011-A+B, 111-A-B, 000-NOT A, 001-A AND B, 010-A OR B, 100-A XOR B
 input[2:0] Op;
 output reg[7:0] Out;
 output Cout;
 output reg Zero;
 output reg Sign;
 
 wire FinalCin; //Final value of Cin fed to ALU after XOR
 wire[7:0] FinalB; //Final value of B fed to ALU after XOR
 wire[7:0] AddResult;
 wire[7:0] LogicResult;
 
 Adder DEV_ADD(A, FinalB, FinalCin, AddResult, Cout);
 LogicUnit DEV_LOG(A, B, Op, LogicResult);
 
 
 assign FinalCin = Cin ^ Op[2]; //Cin XOR Subtract Line
 assign FinalB = B ^ {8{Op[2]}}; //XOR B with Subtract Line
 
 
 always @ (Op or AddResult or LogicResult) begin
 if(Op == 3'b011 || Op == 3'b111)
 Out <= AddResult;
 else if(Op == 3'b000 || Op == 3'b001 || Op == 3'b010 || Op == 3'b100)
 Out <= LogicResult;
 else
 Out <= 8'b00000000;
 end
 
 
 always @ (Out) begin
 if(Out == 8'b00000000)
 Zero <= 1'b1;
 else
 Zero <= 1'b0;
 
 Sign <= Out[7];
 end
endmodule

Adder Module:

module Adder(A, B, Cin, S, Cout);
 input[7:0] A, B;
 input Cin;
 output wire[7:0] S;
 output wire Cout;
 
 assign {Cout, S} = A + B + Cin;
endmodule

Logic Unit:

module LogicUnit(A, B, Op, Out);
 input[7:0] A, B;
 input[2:0] Op; //000-NOT A, 001-A AND B, 010-A OR B, 100-A XOR B
 output reg[7:0] Out;
 
 always @ (Op or A or B) begin
 case(Op)
 3'b000: Out <= ~A;
 3'b001: Out <= A & B;
 3'b010: Out <= A | B;
 3'b100: Out <= A ^ B;
 endcase 
 end
 
endmodule
toolic
14.6k5 gold badges29 silver badges203 bronze badges
asked Apr 29, 2017 at 23:25
\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

Design Issue:

From a design perspective I see one issue in LogicUnit. Out is an inferred latch because it is not assigned in all conditions of Op. The risks associated with these latches and how to use them property (when needed) have already been discussed on other Stack Exchange sites; such as here on Electrical Engineering Stack Exchange and here on Stack Overflow. Synthesis considers all possibilities. Depending on your setup it may or may not recognize Op>4 as out of bounds. It is safer to give it an explicit assignment and allow the synthesizer to choose how to optimize.

In your situation a latch should not be inferred. The easiest way to resolve this is to ass a default statement:

always @* begin
 case(Op)
 3'b000: Out = ~A;
 3'b001: Out = A & B;
 3'b010: Out = A | B;
 3'b100: Out = A ^ B;
 default: Out = A ^ B; // <<-- or any expression to a known value
 endcase
end

You may have noticed I also changes the <= to =. This has to do with coding style recommendations and suggestions that I will cover next.

Coding style:

Blocking vs Non-Blocking Assignments:

The only major coding style concerning I see is that you are using non-blocking assignments (<=) for all assignments in your always blocks. Non-blocking assignments should be used for flops (edge-sensitive) or latches (level-sensitive). Blocking assignments (=) should be used for combinational logic; which is what your logic is.

Blocking and non-blocking update the LHS at different regions in the scheduler. Blocking is immediate evaluation and assignment. Non-blocking is immediate evaluation with postponed assignment. Non-blocking was introduced to resolve a race condition when evaluating and assigning synchronous logic. Using non-blocking inappropriately, such as using it everywhere, can introduce another form of race conditions.

It is recommended to keep blocking and non-blocking assignments in separate always blocks. There are technically legal ways to safely mix them, but most avoid it all together

FYI, by using blocking assignment you could merge the always blocks that assign Out and Zero.

Sensitivity list:

What you have is legal and 100% complaint with the original Verilog IEEE standard (IEEE1364-1995). However automatic sensitivity (@* or @(*)) was added in IEEE1364-2001 and is the preferred and recommended way of declaring conbinational and latching logic with Verilog. @* use the RHS varabiles and nets used in the always-block to determine what signals it needs to be sensitive to. It does not look for signals used inside the body of tasks or functions (signals in the port list are fine) used with in the always block. This is a limitation, but it is also not recommended to have synthesizable tasks/functions reference non-port signals.

@* does more that reduce typing. By allowing verilog to manage the sencitity list for you, it reduces the risk of forgetting a signal (behavior vs synthesis mismatch), having unnecessary signals (possible simulation overhead), and typos (compiling errors or will use the wrong signal).

In summary use @* (or the synonymous @(*)).

Module Instances:

You are using connect by order which is okay for small modules with simple connectivity and whose port list isn't under constant editing. If you use connect by name, the port connections are explicit and it will not matter if the port order is re-arranged. It also helps any human readers of your code understand how the ports are intended to connect.

Adder DEV_ADD( .A(A), .B(FinalB), .Cin(FinalCin), .S(AddResult), .Cout(Cout) );

Module header:

Your module header is is complaint IEEE1364-2001 non-ANSI style (History: in IEEE1364-1995, declaring something to be an output and a reg had to be done in separate statements). I have heard of one case where some tools do not support non-ANSI header with one-line output reg. I have know idea how prevalent this issue is as most new code is written with the ANSI header style. Legacy code and some generated code is written as non-ANSI that is comparable with IEEE1364-1995. ANSI header style is currently the most popular because it is the simplest and cleanest way of managing ports. It becomes more useful as the port list grows and when it is edited often.

You don't need to change your code if you do want to. Be aware if you ever run into an issue with another simulator, synthesizes, or other tool that takes in Verilog. A compiling issue could be because some developers skipped support for one-line output reg for non-ANSI headers. It is technically a bug for the tool but it is unlikely to get much attention. The developers are more focused in adding/fixing/improving higher demand features. Plus there are two work arounds: ANSI and 1995's non-ANSI.

Here is an example of you ALU headed in the three flavors.

ANSI style (popular): IEEE1364-2001 ==> IEEE1800-2012(SystemVerilog)

module ALU( // PORT ORDER, DIRECTION, AND TYPE
 input [7:0] A, B,
 input Cin,
 //011-A+B, 111-A-B, 000-NOT A, 001-A AND B, 010-A OR B, 100-A XOR B
 input [2:0] Op,
 output reg [7:0] Out,
 output Cout,
 output reg Zero,
 output reg Sign );

non-ANSI style (1995 style): IEEE1364-1995 ==> IEEE1800-2012(SystemVerilog)

module ALU(A, B, Cin, Op, Out, Cout, Zero, Sign); // PORT ORDER
 // PORT DIRECTION
 input [7:0] A, B;
 input Cin;
 //011-A+B, 111-A-B, 000-NOT A, 001-A AND B, 010-A OR B, 100-A XOR B
 input [2:0] Op;
 output [7:0] Out;
 output Cout;
 output Zero;
 output Sign;
 // OUTPUT TYPE
 reg [7:0] Out;
 reg Zero;
 reg Sign;

non-ANSI style (2001 style): IEEE1364-2001 ==> IEEE1800-2012(SystemVerilog)

module ALU(A, B, Cin, Op, Out, Cout, Zero, Sign); // PORT ORDER
 // PORT DIRECTION AND TYPE
 input [7:0] A, B;
 input Cin;
 //011-A+B, 111-A-B, 000-NOT A, 001-A AND B, 010-A OR B, 100-A XOR B
 input [2:0] Op;
 output reg [7:0] Out;
 output Cout;
 output reg Zero;
 output reg Sign;
answered May 1, 2017 at 19:24
\$\endgroup\$
0
2
\$\begingroup\$

The previous answer is thorough and makes many great points, which I will not repeat. Here are some more coding style suggestions.

Partitioning

It is great that you partitioned the design using module hierarchy. However, there is no need for Adder to be a separate module because addition is such a trivial operation in Verilog. You can simply use the single assign line directly in the top module.

It is also good that you separated code into multiple always blocks. The always block for Zero and Sign is not needed for such simple logic. They can just be simple continuous assignments:

assign Zero = (Out == 8'b00000000);
assign Sign = Out[7];

In this case, there is no need to declare these signals using the reg type:

output Zero;
output Sign;

Readability

Constants with a large number of digits like:

8'b00000000

are easier to read using an underscore separator:

8'b0000_0000

In this case, since all the digits are the same, the replicated concatenation operator can be used (as you already used it elsewhere)

{8{1'b0}}

Parameters

This comment is useful:

//011-A+B, 111-A-B, 000-NOT A, 001-A AND B, 010-A OR B, 100-A XOR B

But, it is a little hard to read being all on one line, and it is far from the code where it applies.

In Verilog, constants are typically replaced by parameter types. For example:

// ALU functions
localparam ADD = 3'b011;
localparam SUB = 3'b111;
localparam NOT = 3'b000;
localparam AND = 3'b001;
localparam OR = 3'b010;
localparam XOR = 3'b100;
always @ (Op or AddResult or LogicResult) begin
 if (Op == AND || Op == SUB)
 Out <= AddResult;
 else if (Op == NOT || Op == AND || Op == OR || Op == XOR)
 Out <= LogicResult;

DRY

Lines like this:

if (Op == AND || Op == SUB)

have repeated code (Op == ). This can be simplified using the inside operator. This syntax requires you to enable SystemVerilog features in your simulator, if they are not already enabled by default:

if (Op inside {AND, SUB})
answered Jun 19 at 16:27
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.